views:

150

answers:

1

The question is related to financial products, interest rates and two properties that determine which interest a certain product has. But I think fruits and baskets are easier to visualize for this.

First, we have fruit. Fruit can have a specific size (Small, medium, Large) and colour (Red, Green, Blue). These will be two different enumerations. Baskets will contain all kind of fruits, but no two are identical in colour or shape. But every possible combination will be in the basket. Since we have three sizes and three colours, we will end up with 9 pieces of fruits in every basket. If we had 4 colours, we would end up with 12 pieces.

We store the basket information per basket. Every basket has a Dictionary<{Size, Color}, Fruit> which defines all the pieces of fruit in the basket. However, this dictionary might not be completely full, in which case all other combinations are apples. The dictionary only contains fruits of a different kind. (Although they too can contain apples.) Next to apples, we also have pears and banana's. Yeah, they could be red but I would wonder what kind of paint was used to make them red. Remember, this is just to visualize the problem. :-)

Anyway, I now have a list of baskets with per basket a list of fruits. Apples by default, Pears or bananas if they're in the Dictionary. But I need to look at the information from a different angle.

I need to convert this structure to a list of fruits and per fruit the baskets where they can be found, plus the sizes and colours of the specific fruit. Thus, bananas are in basket 1 ({small, yellow}, {small, red}, {medium, red}), basket 3 (...), basket 4, 8 and 10. Same with pears, but I can't have a yellow pear in basket 1, since that one is already defined for a banana.

I have a big advantage: none of these structures are clearly defined yet! However, I need the Basket view as a way to provide the information to the conversion process. And I need the Fruit view for further calculations since I need to do additional math based upon just the fruit itself, not their size or colour or the basket where they're from...

So, any good suggestions for the structure before and after transformation and how to do the transformation itself, using Linq in C#?


In real, the baskets are products. The size and colour are smaller variances on the products. The fruit are interest rates and I only need this rate to do the math. By grouping the product by their interest rate, I can reduce the number of calculations to just a few. We're dealing with 1600+ products with per product about (10x10) 100 different variants, thus a total of 160.000 interest rates. The rates themselves are generally between 3 and 7.5% and rounded to 1/20th of a percent. Thus, about 90 different rates resulting in 90 calculations instead of 160.000 calculations...

And I need to convince management to take this step because they fear it's lots of work, becomes unreadable or will be hard to maintain.


Based on the interest rate, you can calculate how much a person can loan based upon how much he's willing to spend per month on a product with certain conditions. This optimization will allow me to make much faster comparisons between different products. Too bad I'm the first in the company who noticed this interesting optimization! :-)

+2  A: 

Well, once I got started, I had to write the whole code. Maybe it doesn't solve your underlying problem but I think it nails the fruit basket thing.

public enum FruitSize{Small, Medium, Large}
public enum FruitColor {Red, Green, Blue}

  // immutable struct is important for use as dictionary keys
public struct FruitDescription
{
    readonly FruitSize _size;
    public FruitSize Size {get{return _size;}}
    readonly FruitColor _color;
    public FruitColor Color { get { return _color; } }
    public FruitDescription(FruitSize size, FruitColor color)
    {
        _size = size;
        _color = color;
    }
}
    //abstract parent class ...
public abstract class Fruit
{ public FruitDescription Description {get;set;} }
    //... and children
public class Apple : Fruit{}
public class Banana : Fruit{}
public class Pear : Fruit{}

public class Basket
{
    private Dictionary<FruitDescription, Fruit> internalFruits =
        new Dictionary<FruitDescription, Fruit>();

    public void AddFruit(Fruit addme)
    {
        internalFruits[addme.Description] = addme;
    }

    public IEnumerable<FruitDescription> AllDescriptions()
    {
        foreach (FruitSize size in Enum.GetValues(typeof(FruitSize)))
        {
            foreach (FruitColor color in Enum.GetValues(typeof(FruitColor)))
            {
                FruitDescription desc = new FruitDescription(size, color);
                yield return desc;
            }
        }
    }

    public Apple GetDefaultApple(FruitDescription desc)
    {
        return new Apple() { Description = desc };
    }

    public IEnumerable<Fruit> GetFruits()
    {
        IEnumerable<Fruit> result = AllDescriptions()
            .Select(desc =>
              internalFruits.ContainsKey(desc) ?
              internalFruits[desc] :
              GetDefaultApple(desc));
        return result;
    }
}

public class Pair<T, U>
{
    public T First { get; set; }
    public U Second { get; set; }
}

public class TestClass
{
    public static void Test()
    {
        Basket b1 = new Basket();
        b1.AddFruit(new Banana() { Description =
            new FruitDescription(FruitSize.Medium, FruitColor.Blue) });
        b1.AddFruit(new Banana() { Description =
            new FruitDescription(FruitSize.Medium, FruitColor.Green) });

        Basket b2 = new Basket();
        b2.AddFruit(new Pear() { Description =
            new FruitDescription(FruitSize.Medium, FruitColor.Green) });

        List<Basket> source = new List<Basket>();
        source.Add(b1);
        source.Add(b2);

        //the main event - a query.
        List<Pair<Fruit, Basket>> result =
        (
          from basket in source
          from fruit in basket.GetFruits()
          select new Pair<Fruit, Basket>()
          { First = fruit, Second = basket }
        ).ToList();

        //a second results structure for fun
        ILookup<Type, Basket> resultByType = result.ToLookup
        (
            p => p.First.GetType(),
            p => p.Second
        );

        Console.WriteLine("Number of fruit: {0}",
            result.Count);
        Console.WriteLine("Number of apples: {0}",
            resultByType[typeof(Apple)].Count());
        Console.WriteLine("Number of baskets with apples: {0}",
            resultByType[typeof(Apple)].Distinct().Count());
        Console.WriteLine("Number of bananas: {0}",
            resultByType[typeof(Banana)].Count());
        Console.WriteLine("Number of baskets with bananas: {0}",
            resultByType[typeof(Banana)].Distinct().Count());
    }

}

With these results:

Number of fruit: 18
Number of apples: 15
Number of baskets with apples: 2
Number of bananas: 2
Number of baskets with bananas: 1
David B
Call me a fruitcake for creating the question, but it seems it was clear enough to generate the desired fruit salad! :-) The main event query is the most important part, which reverses the view on the baskets from the fruit's perspective. Well done.
Workshop Alex