6
\$\begingroup\$

I have a List of objects, List<myObject> objectList, and each object contains a List of strings like so:

myObject: StringList( "A_1", "B_1", "C_1", "D_1", "E_1", "F_1" )  
myObject: StringList( "A_2", "B_2", "C_1", "D_2", "E_2", "F_1" )  
myObject: StringList( "A_2", "B_3", "C_1", "D_3", "E_2", "F_1" )  

I'm trying to merge the lists into a dictionary of: Dictionary<string, List<string>>, where the final results will look like so:

Dictionary: { [A:1,2]  [B:1,2,3]  [C:1]  [D:1,2,3]  [E:1,2]  [F:1] }

Here is what I have done, and it does work:

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();

foreach (myObject result in objectList)
{
    foreach (var item in result.StringList)
    {
        string Key = item.Split('_')[0];
        string Value = item.Split('_')[1];

        List<string> sValue = new List<string>();
        bool exists = dict.TryGetValue(Key, out sValue);

        if (exists && !sValue.Contains(Value))
        {
            sValue.Add(Value);
            dict[Key] = sValue;
        }
        else if (!exists)
        {
            sValue = sValue ?? new List<string>();
            sValue.Add(Value);
            dict.Add(Key, sValue);
        }
    }
}

Is there a better way to do this, where I don't have to use two foreach loops? Is there a way to do it without using any foreach loops?

I have tried using lambda but this is as far as I got:

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();

foreach (myObject result in objectList)
{
    dict = result.StringList.Select(x => x.Split('_'))
                            .GroupBy(x => x[0])
                            .ToDictionary(x => x.Key, x => x.Select(g => g[1]).ToList());
}

The problem is that the expression keeps overwriting any existing entries in the dictionary each time I iterate through the loop.

Is there a way to keep the existing entries, groupby the Key and add to the existing list of values but don't duplicate any values?

I think the answer lies in this part of the expression:

x => x.Select(g => g[1]).ToList()

but I'm not 100% sure.

\$\endgroup\$

migrated from stackoverflow.com Nov 27 '13 at 16:33

This question came from our site for professional and enthusiast programmers.

  • 1
    \$\begingroup\$ Did you try SelectMany? \$\endgroup\$ – Mike Cheel Nov 27 '13 at 16:28
8
\$\begingroup\$

Just get rid of the outer foreach as well:

dict = objectList.SelectMany(x => x.StringList)
       .Select(x => x.Split('_'))
       .GroupBy(x => x[0])
       .ToDictionary(x => x.Key, x => x.Select(g => g[1]).Distinct().ToList());

SelectMany retrieves all the StringLists and flattens them into one single list:

"A_1", "B_1", ..., "F_1", "A_2", "B_2", ...
\$\endgroup\$
  • \$\begingroup\$ I think you want a distinct in there as well so that A is 1,2 rather than 1,2,2. \$\endgroup\$ – Chris Nov 27 '13 at 16:39
  • 1
    \$\begingroup\$ x => x.Select(g => g[1]).Distinct().ToList() \$\endgroup\$ – Ahmed KRAIEM Nov 27 '13 at 16:40
  • 1
    \$\begingroup\$ Note that Distinct destroys order, so it might be 1,2 or 2,1. \$\endgroup\$ – Tim S. Nov 27 '13 at 16:48
  • \$\begingroup\$ @Heinzi - Thanks so much! This works perfectly! Much simpler and more elegant. For the life of me, I couldn't figure this out...a million times thank you! \$\endgroup\$ – user3042376 Nov 27 '13 at 16:59
  • 1
    \$\begingroup\$ @Groo Like a comment at my link says, "what you say could be true [that it preserves first-found order], but it would be a bad idea to rely on that behavior" (because it is documented as being an unordered sequence, you should treat it as such). Also, I don't know that objectList had it sorted to begin with, the example just happens to have it so (I think). \$\endgroup\$ – Tim S. Nov 27 '13 at 17:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.