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.
-
1\$\begingroup\$ Did you try SelectMany? \$\endgroup\$Mike Cheel– Mike Cheel2013年11月27日 16:28:38 +00:00Commented Nov 27, 2013 at 16:28
1 Answer 1
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 StringList
s and flattens them into one single list:
"A_1", "B_1", ..., "F_1", "A_2", "B_2", ...
-
\$\begingroup\$ I think you want a distinct in there as well so that A is
1,2
rather than1,2,2
. \$\endgroup\$Chris– Chris2013年11月27日 16:39:51 +00:00Commented Nov 27, 2013 at 16:39 -
1\$\begingroup\$
x => x.Select(g => g[1]).Distinct().ToList()
\$\endgroup\$Ahmed KRAIEM– Ahmed KRAIEM2013年11月27日 16:40:02 +00:00Commented Nov 27, 2013 at 16:40 -
1\$\begingroup\$ Note that
Distinct
destroys order, so it might be1,2
or2,1
. \$\endgroup\$Tim S.– Tim S.2013年11月27日 16:48:17 +00:00Commented 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\$user3042376– user30423762013年11月27日 16:59:17 +00:00Commented 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\$Tim S.– Tim S.2013年11月27日 17:07:53 +00:00Commented Nov 27, 2013 at 17:07