mike7mike
mike7mike

Reputation: 433

C# Merge properties from two (or more) lists based on common property

I have three lists of objects each of which are linked to one other list by a single common property, Parent and Node in the following hierarchy: Model -> Intermediate -> Results. I have the following code:

class Result
{
    public string Name { get; set; }
    public int Node{ get; set; }
    public int Parent{ get; set; }
}
class Intermediate
{
    public int Node{ get; set; }
    public int Parent{ get; set; }
}
class Model
{
    public string Name { get; set; }
    public int Node{ get; set; }
    public int Parent{ get; set; }
}

public class Example
{
    public static void Main()
    {
        List<Result> results = new List<Result>();
        List<Intermediate> intermediates = new List<Intermediate>();
        List<Model> models = new List<Model>();

        // Example objects in the list
        results.Add(new Result() { Name = "", Parent = 21, Node = 101 });
        intermediates.Add(new Part() { Parent = 11, Node = 21 });
        models.Add(new Part() { Name = "ABCD", Parent = 1, Node = 11 });
        ...
    }
}

As can be seen the Model object links to Intermediate object via model.Node and intermediate.Parent, and the Intermediate object links to Results object via intermediate.Node and results.Parent. Note the lists can contain thousands of items, each added using a similar format as above.

What I want to be able to do is add the names from the Model objects in list to the matching Results objects in the results list.

My thinking is that I can loop through each object in the Intermediate list and find (using LINQ) the Result object where intermediate.Node = result.Parent, and then either replace the value of the result.Parent with intermediate.Parent, or add a new Grandparent property to the Result object in which to put the intermediate.Parent. Then repeat the process looping through each of the objects in the models list finding the matching Result object and adding the Name.

So I guess my question is, is this the best way of doing this, or is there a more efficient way? I have many lists where the same will have to be repeated, so was wondering if there was a better way as it can be quite slow looping through every object. Also is there a way to get from the first list directly to the third list.

I hope this is well enough explained. I am quite a beginner when it comes to C#.

Upvotes: 0

Views: 2576

Answers (3)

mehmet mecek
mehmet mecek

Reputation: 2685

Smells like Composite Pattern what you talk about here.

And you can use HashSet to keep your values to perform it fast.

public class Item
{
    public Item(int itemNode)
    {
        Node = itemNode;
        Children = new HashSet<Item>();
    }

    public int Node { get; set; }
    public Item Parent { get; set; }
    private HashSet<Item> Children { get; set; }

    public bool Add(Item item)
    {
        item.Parent = this;
        return Children.Add(item);
    }

    public List<Item> Find(Func<Item, bool> predicate)
    {
        var found = new List<Item>();
        if (predicate(this)) found.Add(this);
        Collect(predicate, found);
        return found;
    }

    public void Collect(Func<Item, bool> predicate, List<Item> collected = null)
    {
        collected = collected ?? new List<Item>();
        collected.AddRange(Children.Where(predicate).ToList());

        foreach (var child in Children)
        {
            child.Collect(predicate, collected);
        }
    }
}

public class Model : Item //this is your model
{
    public string Name { get; set; }
    public Model(int itemNode, string name) : base(itemNode)
    {
        Name = name;
    }

    public List<Item> GetNamesMatchingWith(Func<Item, bool> predicate)
    {
        return Find(predicate);
    }
}

public class Example
{
    public static void Main()
    {
        var root = new Model(0, "root");
        var one = new Model(1, "1");
        var two = new Model(2, "2");
        var tree = new Model(3, "3");

        root.Add(one);
        root.Add(two);
        root.Add(tree);

        var a = new Model(4, "a");
        var b = new Model(5, "b");

        two.Add(a);
        two.Add(b);

        var namesMatchingWith = root.GetNamesMatchingWith(x=> x.Parent!=null && x.Parent.Node == 2);
        Console.ReadKey();
    }
}

Hope it inpires you..

Upvotes: 0

Robert McKee
Robert McKee

Reputation: 21487

You actually have Results -> Intermediate -> Model instead of Model -> Intermediate -> Results.

To speed the process of removing the Intermediate, build a dictionary. Then you can do a simple select on Results using the dictionary to convert.

var intermediateDict=intermediates.ToDictionary(key=>key.Node,val=>val.Parent);
var newresults=results.Select(r=>new Result {
  Name=r.Name,
  Node=r.Node,
  Parent=intermediateDict[r.Parent]
  });

You can also do joins to get the final answer.

Upvotes: 3

Martin Staufcik
Martin Staufcik

Reputation: 9490

It looks a foreach loop can be used to get to the result (leaf) nodes and assign the model name to result nodes:

var theModel = models.First(); // select a model

foreach (var interm in intermediates.Where(x => x.Parent == theModel.Node))
{
    foreach (var res in results.Where(x => x.Parent == interm.Node))
    {
        res.Name = theModel.Name;
    }
}

Upvotes: 1

Related Questions