7
\$\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.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Nov 27, 2013 at 16:25
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Did you try SelectMany? \$\endgroup\$ Commented Nov 27, 2013 at 16:28

1 Answer 1

10
\$\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", ...
answered Nov 27, 2013 at 16:32
\$\endgroup\$
10
  • \$\begingroup\$ I think you want a distinct in there as well so that A is 1,2 rather than 1,2,2. \$\endgroup\$ Commented Nov 27, 2013 at 16:39
  • 1
    \$\begingroup\$ x => x.Select(g => g[1]).Distinct().ToList() \$\endgroup\$ Commented Nov 27, 2013 at 16:40
  • 1
    \$\begingroup\$ Note that Distinct destroys order, so it might be 1,2 or 2,1. \$\endgroup\$ Commented Nov 27, 2013 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\$ Commented Nov 27, 2013 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\$ Commented Nov 27, 2013 at 17:07

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.