Showing posts with label foreach. Show all posts
Showing posts with label foreach. Show all posts

Thursday, 7 February 2013

Problem with delegate within foreach

The picture shown below represents my data. Data that I have and the expected result at the end:




The logic being, that for every date in the list of dates, find the associated price tuple where the effective date is less than or equal to the date and is the max date.

The structure of Price is shown below.

 
public class Price
{
    public int PriceId { get; set; }

    public DateTime EffectiveDate { get; set; }

    public double PriceAmount { get; set; }

    public DateTime CreatedOn { get; set; }
}

I have a static method that gets me the data that I need
 
static List<Price> GetPrices()
{
    return new List<Price>()
    {
        new Price()
        {
            PriceId = 1, EffectiveDate = new DateTime(2012,1,1),
            PriceAmount = 100, CreatedOn = new DateTime(2012,1,1)
        },
        new Price()
        {
            PriceId = 2, EffectiveDate = new DateTime(2012,1,1),
            PriceAmount = 200, CreatedOn = new DateTime(2012,1,1)
        },
        new Price()
        {
            PriceId = 3, EffectiveDate = new DateTime(2012,1,2),
            PriceAmount = 300, CreatedOn = new DateTime(2012,1,2)
        },
        new Price()
        {
            PriceId = 4, EffectiveDate = new DateTime(2012,1,3),
            PriceAmount = 400, CreatedOn = new DateTime(2012,1,3)
        },
        new Price()
        {
            PriceId = 5, EffectiveDate = new DateTime(2012,1,4),
            PriceAmount = 500, CreatedOn = new DateTime(2012,1,4)
        },
        new Price()
        {
            PriceId = 6, EffectiveDate = new DateTime(2012,1,5),
            PriceAmount = 600, CreatedOn = new DateTime(2012,1,5)
        },
        new Price()
        {
            PriceId = 7, EffectiveDate = new DateTime(2012,1,16),
            PriceAmount = 700, CreatedOn = new DateTime(2012,1,16)
        },
        new Price()
        {
            PriceId = 8, EffectiveDate = new DateTime(2012,1,27),
            PriceAmount = 800, CreatedOn = new DateTime(2012,1,27)
        },
        new Price()
        {
            PriceId = 9, EffectiveDate = new DateTime(2012,1,28),
            PriceAmount = 900, CreatedOn = new DateTime(2012,1,28)
        },
    };
}

This is the original code that I wrote to get me the information that I needed:

 
// This represents your List of dates
var listOfDates = new List<DateTime>()
                        {
                        new DateTime(2012,1,2),
                        new DateTime(2012,1,10),
                        new DateTime(2012,1,15),
                        new DateTime(2012,1,27)
                        };

// This represents your prices
var prices = GetPrices();

// Initialise the expected result set
var expectedResultSet = new List<Price>();

// Get your expected result set
listOfDates.ForEach(date =>
                        {
                            // In each date, find the price that you're after
                            var price = prices
                                .Where(p => p.EffectiveDate <= date)
                                .OrderByDescending(result => result.EffectiveDate)
                                .FirstOrDefault();

                            if(price != null)
                            {
                                // Fix the Date
                                price.EffectiveDate = date;

                                // Add it to the expected result
                                expectedResultSet.Add(price);
                            }
                        });

// Print the Result
Print(expectedResultSet);

If you check the output, this does NOT give you the result you were expecting!!!
Now to get the result you're after, you'll need to iterate through the list in a different manner:

(This is just one option ... )

 
// Get Dates and Prices
listOfDates = GetListOfDates();
prices = GetPrices();

// Step A: Get a Tuple of Date & Price
List<Tuple<DateTime, Price>> datesWithAssociatedPrice;
datesWithAssociatedPrice = listOfDates
    // Step 1: Select the Requested Date and Price(S) 
    // where the Price's Effective Date <= the date (from range)
    // [One Date .. Many Prices]
.Select(eachDate => new
{
    RequestedDate = eachDate,
    AssociatedPrices = prices.Where(p => p.EffectiveDate <= eachDate)
})

// Step 2: Now iterate through the list of Date, List & in each tuple,
    // find the first instance of Price with the Max Effective date
.Select(dateAndPrice => new
{
    RequestedDate = dateAndPrice.RequestedDate,
    AssociatedPrice = dateAndPrice
        .AssociatedPrices
        .First(price => price.EffectiveDate ==
            dateAndPrice.AssociatedPrices.Max(item => item.EffectiveDate))
})

// Step 3: Now get the data you need as a List<Tuple<DateTime, Price>> tuple
.Select(tuple => 
    new Tuple<DateTime, Price>(tuple.RequestedDate, 
        tuple.AssociatedPrice)).ToList();

// Print using an extension method :)
Print(datesWithAssociatedPrice.ToPriceList());

And now the result is what is expected.


The complete source is available here: http://snipt.org/zQjc0#expand


References: http://stackoverflow.com/questions/2571398/problem-with-anonymouse-delegate-within-foreach


Thursday, 6 December 2012

Find Overlapping Date Ranges



I recently had to implement a "DateRange" validator to check for any overlaps in the date ranges.

Here is my approach to it :

I started with a custom DateRange class.
 
public class DateRange
{
    public int SortOrder { get; set; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    public DateRange() {}

    public DateRange(DateTime sTime, DateTime eTime)
    {
        Start = sTime;
        End = eTime;
    }
} 

Then I call the "HasOverlap" method on my validator to check for overlaps.
 
/*
* LOGIC:
* 
* 1. Take a List of date ranges. Example:
            [Tuple A]         --------         [Sort Order 1]
            [Tuple B]--------                  [Sort Order 2]
            [Tuple C]     --------             [Sort Order 3]
            [Tuple D]     --------             [Sort Order 4]
            [Tuple E]             --------     [Sort Order 5]
            [Tuple F]     ----------           [Sort Order 6]
         
* 2. Now sort the range by start + End Dates. This results in:
            [Tuple B]--------                  [Sort Order 1]
            [Tuple C]     --------             [Sort Order 2]
            [Tuple D]     --------             [Sort Order 3]
            [Tuple F]     ----------           [Sort Order 4]
            [Tuple A]         --------         [Sort Order 5]
            [Tuple E]             --------     [Sort Order 6]    
         
* 3. The Logic is that there will be an overlap IF EVEN ONE of 
*    the 2 base conditions are met:
*      (a) After sorting the list, a given tuple (TUPLE X) 
*          will be deemed overlapping 
*              if the END-DATE of (TUPLE X) is GREATER THAN the START-DATE 
*              of ANY tuple WHERE the sort order is GREATER THAN that of (TUPLE X)
*          
*      --- OR ---
*      
*      (b) After sorting the list, a given tuple (TUPLE X) 
*          will be deemed overlapping 
*              if the START-DATE and END-DATE of (TUPLE X) MATCHES 
*              the START-DATE and END-DATE of ANY tuple 
*              WHERE the sort order is GREATER THAN that of (TUPLE X)
*/
private bool HasOverlap(IList<DateRange> ranges)
{
    // 1. Sort Dates based on Start & End Dates
    var sortedRange = ranges
                        .OrderBy(p => p.Start)
                        .ThenBy(p => p.End)
                        .ToList();

    var sortCounter = 0;
    sortedRange.ForEach(e =>
    {
        e.SortOrder = sortCounter;
        sortCounter++;
    });


    // 2. Check if the end dates are > any start dates except the same one 
    return sortedRange
        .Any(tuple => (from innerLoop in sortedRange
                        where (
                                tuple.End > innerLoop.Start &&
                                innerLoop.SortOrder > tuple.SortOrder
                                ) ||
                                (
                                tuple.End == innerLoop.End &&
                                tuple.Start == innerLoop.Start &&
                                innerLoop.SortOrder > tuple.SortOrder
                                )
                        select innerLoop).Any());
}

Additionally I also pass my DateRanges through a sanity check to ensure that the start < end dates.