3

I've done a lot of digging and I can't seem to find an answer to this particular problem; answers to similar problems, but nothing quite like this.

Essentially, what I'm trying to do is take two C# lists, both of which contain a range of C# Objects comprised of string-integer pairs, then merge them together, joining the values of similar Objects.

Let's say my Object Class looks like this;

public class Object
{
    public string Name;
    public int Value;
}

And we'll also say that my lists look like this;

    objectList1             objectList2
 Name        Value       Name        Value
Object1        1        Object1        1
Object2        1        Object2        1
Object3        1
Object4        1
Object5        1

My goal here is to combine the contents of objectList2's Objects to the corresponding Objects in objectList1. In this example, that would mean that objectList1 would look like this when all is said and done;

 Name        Value
Object1        2
Object2        2
Object3        1
Object4        1
Object5        1

Now I know this is possible to do with foreach and FindIndex() calls, but I'm almost positive there's a more efficient way to go about this. If I'm wrong, that's fine, but I'd like to know if I can optimize this because my gut tells me there has to be a simpler way.

2
  • You need to supply metrics for efficient and optimize. Fewer lines of code? Fewer level 2 cache misses? Fewer string comparisons? Lower memory usage? Show the code you're using and describe the problem you're having. Then we can help your gut. Commented Nov 10, 2016 at 2:10
  • I was mostly referring to the inherent inefficiency of foreach cycles. I went with the Dictionaries option because instant location via key values is far more effective in this case. Commented Nov 10, 2016 at 2:28

3 Answers 3

3

I believe what you're looking to do:

var contents = sample.Concat(example).GroupBy(n => n.Name);

Basically it will create a grouping, but it will append the list together. Then group based on the name, which will provide a numeric indicator of how many of the same name exist. Basically, you end up with a IGrouping<Key, Value>.

For instance:

var merge = new List<Content>();
var contents = sample.Concat(example).GroupBy(n => n.Name);
foreach(var item in contents)
     merge.Add(new Content() { Name = item.Key, Value = item.Sum(value => value.Value) });

You could also, do it with all Linq if you wanted:

    var contents = example.Concat(sample)
        .GroupBy(n => n.Name)
        .SelectMany(content => 
                    content.Select((item, i) => new { Name = content.Key, Value = content.Sum(v => v.Value) }).Distinct());
Sign up to request clarification or add additional context in comments.

Comments

1

Best bet I can think of is to use Dictionary, assuming all keys are unique

do a foreach on the smaller Dictionary (not shown below), checking for matching keys in the other, and then modifying if found or adding if not found (note this algorithm is destructive, make a copy to return if you want to preserve the originals)

    void Merge (Dictionary<string, int> a, Dictionary<string, int> b)
    {
        foreach (string key in a.Keys)
        {
            if (b.ContainsKey(key))
            {
                b[key] = b[key] + a[key];
            }
            else
            {
                b.Add(key, a[key]);
            }
        }
    }

2 Comments

I don't know why I wasn't using Dictionaries in the first place >.< It's clearly the best route to go in this scenario! Thank you so much!
@PorkchopDonut My answer has an all Linq and a tad bit simpler approach if you'd like. May not be as efficient though.
1

You can use LINQ as below

 var list1 = new List<Object>();
list1.Add(new Object { Name = "Object1", Value = 1 });
list1.Add(new Object { Name = "Object2", Value = 1 });
list1.Add(new Object { Name = "Object3", Value = 1 });
list1.Add(new Object { Name = "Object4", Value = 1 });
list1.Add(new Object { Name = "Object5", Value = 1 });
var list2 = new List<Object>();
list2.Add(new Object { Name = "Object1", Value = 1 });
list2.Add(new Object { Name = "Object2", Value = 1 });

var total = from item1 in list1
            join item2 in list2
            on item1.Name equals item2.Name into list3
            from subset in list3.DefaultIfEmpty()
            select new Object
            {
                Name = item1.Name,
                Value = item1.Value + (subset == null ? 0 : subset.Value)
            };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.