How to get except between two Dictionaries<int, IEnumerable<string>>? - c#

I have two Dictionaries>
var test1 = await GetTest1();
var test2 = await GetTest2();
var groupedTest1 = test1.GroupBy(j => j.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
var groupedTest2 = test2.GroupBy(a => a.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
And I need two get the difference between them.
For example:
var result = groupedTest1.Except(groupedTest2);
If groupedTest1 contains something IEnumerable which contains in groupedTest2 I don't need to include this. Besides, I need to include check only for a similar key. My question is:
How can I do it?
groupedTest1 : { { Key: 1, IEnumerable: "test1, test2" },
{ Key: 2, IEnumerable: "test3, test4" } } groupedTest2 : {
{ Key: 2, IEnumerable: "test3, test4" } }
result should be Key: 1, IEnumerable: "test1, test2"

If I understand it you effectively have 2 actions; first, get all the keys that don't match and their entire entry should be in the result, and second for matching keys only get the values that don't match. I've put together a little console app to display this:
static void Main(string[] args)
{
//2 test dictionaries. Key 1 and it's values should be displayed and Key 3 with only "test5".
var test1 = new Dictionary<int, IEnumerable<string>>
{
{ 1, new List<string>{ "test1", "test2" } },
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4", "test5" } }
};
var test2 = new Dictionary<int, IEnumerable<string>>
{
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4" } }
};
//get non-matching keys first.
Console.WriteLine("Non-matching keys:");
var entriesWithNoMatch = test1.Where(x => !test2.ContainsKey(x.Key));
foreach (var entry in entriesWithNoMatch)
{
WriteResults(entry.Key, entry.Value.ToList());
}
//get matching keys and compare values
Console.WriteLine("Matching keys, non-matching values:");
var matchingKeys = test1.Keys.Intersect(test2.Keys);
foreach (var match in matchingKeys)
{
var result = new KeyValuePair<int, IEnumerable<string>>(match, test1[match].Except(test2[match]));
if (result.Value.Count() > 0)
{
WriteResults(match, result.Value.ToList());
}
}
Console.ReadLine();
}
//simple method to loop through results displaying key and results.
static void WriteResults(int key, IList<string> results)
{
var sb = new StringBuilder();
foreach (var r in results)
{
sb.Append(r + ",");
}
if (sb.Length > 1)
{
sb.Remove(sb.Length - 1, 1);
}
Console.WriteLine("Key: " + key.ToString() + ", results: " + sb.ToString());
sb.Clear();
}
Results:

Related

Using Group by with x amount of elements

Here's a list, think of it as rows and columns where rows are going down and columns are side ways. the column count will always be the same for all rows.
var dataValues = new List<List<string>>()
{
//row 1
new List<string>(){"A","12","X","P8" },
//row 2
new List<string>(){"B","13","Y","P7" },
//row 3
new List<string>(){"C","12","Y","P6" },
//row 4
new List<string>(){"A","14","X","P5" },
//....
new List<string>(){"D","15","Z","P4" },
new List<string>(){"A","13","X","P3" },
new List<string>(){"B","14","Y","P2" },
new List<string>(){"C","13","Z","P1" },
};
The user providers a list of indexes to group by.
var userParam= new List<int>() { 0, 2 };
my question is how do i dynamically group dataValues by the userParam where user param is n amount of index. In the example above it will gorup by the first column and the 3rd. However the index can change and the amount of indexes can change aswell
example
var userParam2 = new List<int>() { 0, 2};
var userParam3 = new List<int>() { 0};
var userParam4 = new List<int>() { 0,1,2};
i know how to group by when i know how many indexes there will be (the the case below it's 2 index parameters), however when it's dynamic (x amount) then i do not know how to do this
var result = dataValues.GroupBy(e => new { G1 = e[userParam2 [0]], G2 = e[userParam2 [1]] });
You could use a Custom Comparer to achieve this :
1 - Declaration of GroupByComparer that inherit from IEqualityComparer :
public class GroupByComparer : IEqualityComparer<List<string>>
{
private static List<int> _intList;
public GroupByComparer(List<int> intList)
{
_intList = intList;
}
public bool Equals(List<string> x, List<string> y)
{
foreach (int item in _intList)
{
if (x[item] != y[item])
return false;
}
return true;
}
public int GetHashCode(List<string> obj)
{
int hashCode = 0;
foreach (int item in _intList)
{
hashCode ^= obj[item].GetHashCode() + item;
}
return hashCode;
}
}
2 - Call group by with EqualityComparer like :
var userParam = new List<int>() { 0, 2 };
var result = dataValues.GroupBy(e => e, new GroupByComparer(userParam));
I hope you find this helpful.
I believe i have something but this looks slow please let me know if there is anyway better of doing this.
var userParams = new List<int>() { 0, 2 };
var dataValues = new List<List<string>>()
{
new List<string>(){"A","12","X","P8" },
new List<string>(){"B","13","Y","P7" },
new List<string>(){"C","12","Y","P6" },
new List<string>(){"A","14","X","P5" },
new List<string>(){"D","15","Z","P4" },
new List<string>(){"A","13","X","P3" },
new List<string>(){"B","14","Y","P2" },
new List<string>(){"C","13","Z","P1" },
};
var result = new List<(List<string> Key, List<List<string>> Values)>();
result.Add((new List<string>(), dataValues));
for (int index = 0; index < userParams.Count; index++)
{
var currentResult = new List<(List<string> Key, List<List<string>> Values)>();
foreach (var item in result)
{
foreach (var newGroup in item.Values.GroupBy(e => e[userParams[index]]))
{
var newKey = item.Key.ToList();
newKey.Add(newGroup.Key);
currentResult.Add((newKey, newGroup.ToList()));
}
}
result = currentResult;
}
foreach(var res in result)
{
Console.WriteLine($"Key: {string.Join(#"\", res.Key)}, Values: {string.Join(" | ", res.Values.Select(e=> string.Join(",",e)))}");
}
final result
Key: A\X, Values: A,12,X,P8 | A,14,X,P5 | A,13,X,P3
Key: B\Y, Values: B,13,Y,P7 | B,14,Y,P2
Key: C\Y, Values: C,12,Y,P6
Key: C\Z, Values: C,13,Z,P1
Key: D\Z, Values: D,15,Z,P4

how to set Number from 0 to increment 1,2,,... etc for combine dictionary

I have 2 dictionaries where Number = 0 for all items,
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
Now after eliminating duplicate key/value pairs, in combined dictionary result I want to set Number = 1, 2, 3,... how to do this?
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can do
var n = 0;
and then do it functionally but not very efficiently in your case. The function will select all items from your dictionary and create a new collection with updated values, which is then converted to a dictionary.
var newDict = dict2.Select(d => new Test { Number = ++n, Name = d.Value[1].Name }).ToDictionary();
Or with a good old loop:
foreach(var d in dict2)
{
d.Value[0].Number = n++;
}
As suggested by the comment. If you want to start off with 0, use
n++;
if with 1, use
++n;
Try this:
int i = 0;
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.OrderBy(kvp => kvp.Key)
.ToDictionary(kvp => kvp.Key, kvp => new Test() { Number = ++i, Name = kvp.First().Value.Name });
It should give you this:
{ "Key1", new Test { Number = 1, Name = "Name1" } },
{ "Key2", new Test { Number = 2, Name = "Name2" } },
{ "Key3", new Test { Number = 3, Name = "Name3" } }
{ "Key4", new Test { Number = 4, Name = "Name4" } }
I think you're trying to combine dictionaries, and then assign to Number the count of duplicate items.
You might consider putting all the dictionaries into a list and iterating over each item and putting it in a combined result dictionary. If the item already exists in the result then increment the Number property.
Initial setup:
public class Test
{
public int Number { get; set; }
public string Name { get; set; }
}
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
// Put the dictionaries you want to combine into one list:
var all = new List<Dictionary<string, Test>>();
all.Add(dict1);
all.Add(dict2);
// Declare result dictionary
var combine = new Dictionary<string, Test>();
Set up is done, this is the main loop you want:
foreach (var dict in all)
{
foreach (var kvp in dict)
{
if (combine.ContainsKey(kvp.Key))
{
combine[kvp.Key].Number++;
}
else
{
combine.Add(kvp.Key, kvp.Value);
}
}
}
Interactive shell output:
Dictionary<string, Submission#0.Test>(4) {
{ "Key1", Submission#0.Test { Name="Name1", Number=1 } },
{ "Key2", Submission#0.Test { Name="Name2", Number=0 } },
{ "Key3", Submission#0.Test { Name="Name3", Number=0 } },
{ "Key4", Submission#0.Test { Name="Name4", Number=0 } }
}
There is an overload of Select extension which provides index as well (MSDN)
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select((grp,index) => new { Key = grp.Key, Value = new Test { Number = index+1, Name = grp.First().Name}})
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can loop through the keys
int n = 1;
foreach (string key in combine.Keys) {
combine[key].Number = n++;
}
The keys are not returned in order. If you want to number them in order:
int n = 1;
var orderedKeyValuePairs = combine
.OrderBy(kvp => kvp.Key, StringComparer.CurrentCultureIgnoreCase);
foreach (var kvp in orderedKeyValuePairs) {
kvp.Value.Number = n++;
}
Note that you can only access the Number like this if Test is a reference type (class). If Test was a struct, you would have to re-assign the whole struct because the dictionary would return a copy of the value.
The optional StringComparer argument allows you to specify different string comparison modes:
Ordinal, OrdinalIgnoreCase, CurrentCulture, CurrentCultureIgnoreCase
If you want to sort by name:
int n = 1;
var orderedValues = combine.Values
.OrderBy(v => v.Name, StringComparer.CurrentCultureIgnoreCase);
foreach (var v in orderedValues) {
v.Number = n++;
}
Looping over the key-value-pairs or values also has the advantage that you can change the value directly, whereas when looping through the keys (as in my first code snippet), you must look up the dictionary, which is less performing.

Can't find the appropriate operators for a Linq query

I need help building a Linq query. I have this dictionary :
var dict = new Dictionary<string, IDictionary<int, double>>
{
{ "one", new Dictionary<int, double>
{
{ 1, 2.0 },
{ 2, 3.0 }
}},
{ "two", new Dictionary<int, double>
{
{ 1, 3.0 },
{ 2, 4.0 },
{ 3, 5.0 }
}},
{ "three", new Dictionary<int, double>
{
{ 1, 4.0 },
{ 2, 5.0}
}}
};
I want to select all "string"/"int" tuples whose associated value is 3.0. With Foreach loops, it looks like :
var res = new Dictionary<string, int>();
foreach (var elem in dict.Select (d => new { S = d.Key, I = d.Value }))
{
foreach (var val in elem.I)
{
if (val.Value == 3.0)
{
res.Add(elem.S, val.Key);
}
}
}
I'm trying to do the same with a single Linq query but with no success (I don't know how to "join" the key with the value from a subquery). How would you do this?
Thank you in advance!
You could do it this way:
var result = dict
// flatten to 3-value tuple
.SelectMany(kvp => kvp.Value
.Select(kvp2 => new // anonymous object
{
OuterKey = kvp.Key, // e.g. "one"
InnerKey = kvp2.Key, // e.g. 1
InnerValue = kvp2.Value // e.g. 2.0
})
).Where(o => o.InnerValue == 3.0) // limit to the 3.0 value
.ToDictionary(o => o.OuterKey, o => o.InnerKey)
;
The SelectMany flattens the dictionary of dictionaries into a structure that looks like this:
{ OuterKey = "one", InnerKey = 1, InnerValue = 2.0 },
{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 2, InnerValue = 4.0 },
{ OuterKey = "two", InnerKey = 3, InnerValue = 5.0 },
{ OuterKey = "three", InnerKey = 1, InnerValue = 4.0 },
{ OuterKey = "three", InnerKey = 2, InnerValue = 5.0 }
The Where() limits it down to just the objects with InnerValue = 3.0:
{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 }
The ToDictionary() looks like this:
{ "one", 2 },
{ "two", 1 }
If it's possible for there to be more than a single 3.0 under the same outer key, then you can use ToLookup() instead of ToDictionary().
Here's another butt-ugly way to do it:
var results = from key1 in dict.Keys
let valueDict = dict[key1]
from key2 in valueDict.Keys
where valueDict[key2] == 3d
select new { Key1 = key1, Key2 = key2 };
This would do the trick:
var res = dict
.Where(outer => outer.Value.Any(inner => inner.Value == 3.0))
.ToDictionary(outer => outer.Key, outer => outer.Value.First(x=>x.Value == 3.0).Key);
or to make the code a bit more generic:
var predicate = new Func<KeyValuePair<int, double>, bool>(inner => inner.Value == 3.0);
var res = dict
.Where(outer => outer.Value.Any(inner => predicate(inner)))
.ToDictionary(outer => outer.Key, outer => outer.Value.First(inner => predicate(inner)).Key);

get combination from a list of key-values

I have a dict<string, list<string>>, say 3 keys in dict, the first key has 2 values, the secodn 3 values, the third key has 3 values. If I get a value from each value set, then I will have a combination of 2*3*3 = 18 sets
How to code in c#?
thanks
Edit
Sorry did not make it clear
I want something like this
say I have dict like this
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3", new List<string>() {"f", "g"}
I want output like this
acf, acg, adf, adg, aef, aeg
bcf, bcg, bdf, bdg, bef, beg
With Linq:
var dict = new Dictionary<String, List<String>>() {
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3",new List<String>(){"f", "g", "h"}},
};
var combis = from kv in dict
from val1 in kv.Value
from val2 in kv.Value
select string.Format("{0}{1}", val1, val2);
foreach (var combi in combis)
Console.WriteLine(combi);
demo: http://ideone.com/nm7mY
Quick & dirty but you may polish this method. The result list contains expected result:
Usage:
var dict = new Dictionary<String, List<String>>() {
{"1",new List<String>(){"a", "b"}},
{"2",new List<String>(){"c", "d", "e"}},
{"3",new List<String>(){"f", "g"}},
};
var collections = dict.Select(kvp => kvp.Value).ToArray();
var result = new List<string>();
GetNextProduct(collections, 0, String.Empty, result);
Method that produces the result:
private static void GetNextProduct(IEnumerable<string>[] collections, int collectionIndex, string currentProduct, IList<string> results)
{
var currentList = collections[collectionIndex];
bool isLast = collections.Length == collectionIndex + 1;
foreach (var s in currentList)
{
if (isLast) results.Add(currentProduct + s);
else GetNextProduct(collections, collectionIndex + 1, currentProduct + s, results);
}
}
I think you mean this?
Dictionary<string, int> dict = new Dictionary<string, int>
{
{ "Hello World", 1 },
{ "HelloWorld", 1 },
{ "Hello World", 1 },
};
foreach (var item in dict) // var is of type KeyValuePair<string, int>
Console.WriteLine(item.Key + ", " + item.Value);
Dictionary<string, List<int>> storage = new Dictionary<string, List<int>>();
storage.Add("key1", new List<int>() { 2, 7 });
storage.Add("key2", new List<int>() { 8, 4, 1});
storage.Add("key3", new List<int>() { 3, 9, 3 });
foreach (string key in storage.Keys)
{
//access to single storage...
List<int> subStorage = (List<int>)storage[key];
foreach (int item in subStorage)
{
//access to single value inside storage...
}
}
I would try something like the following if I was trying to read or edit the values in the lists:
Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>();
var arrayOfValues = dict.Values.ToArray();
for (int i = 0; i < arrayOfValues.Length; i++)
{
for (int j = 0; j < arrayOfValues[i].Count; j++)
{
//read/edit arrayOfValues[i][j];
}
}
You do not need recursion since you know the dept of the "tree".

how to filter dictionary values if its key exists in a string[] using linq

string[] strLst = { "One", "Three" };
Dictionary<string, Customer> dicDocTypes = new Dictionary<string, Customer>();
dicDocTypes.Add("One", new Customer { Id = 1, Name = "Rabi" });
dicDocTypes.Add("Two", new Customer { Id = 2, Name = "Shuvankar" });
dicDocTypes.Add("Three", new Customer { Id = 3, Name = "Krishna" });
dicDocTypes.Add("Four", new Customer { Id = 4, Name = "Suresh" });
var rootNodes = from node in dicDocTypes
where node.Key.Contains(**strLst**)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
Question: how to filter dictionary items if the keys matches with a string[]
Instead of trying to ask the key if it belongs to an array, you can ask the array if it contains the key:
var rootNodes = from node in dicDocTypes
where strLst.Contains(node.Key)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
If you have a lot of items, it will be faster to use a HashSet instead of an array (O(1) lookups instead of O(n) lookups):
var strLst = new HashSet<string> { "One", "Three" };
Also, you can simplify the select to this:
var rootNodes = dicDocTypes.Where(n => strLst.Contains(n.Key));
Additionally, there's a cleaner way to initialize dictionaries:
var dicDocTypes = new Dictionary<string, Customer> {
{ "One", new Customer { Id = 1, Name = "Rabi" } },
{ "Two", new Customer { Id = 2, Name = "Shuvankar" } },
{ "Three", new Customer { Id = 3, Name = "Krishna" } },
{ "Four", new Customer { Id = 4, Name = "Suresh" } }
};
As Cameron has said, you're just asking the question the wrong way round. However, you can also make your code simpler - the "node" in a dictionary is already a KeyValuePair, so all you need is:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key));
If you need a new dictionary at the end, you can use the ToDictionary method:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key))
.ToDictionary(pair => pair.Key, pair => pair.Value);
If your list of strings to filter by becomes longer, you might want to consider making it a HashSet<string> instead of an array.
var rootNodes = dicDocTypes.Join(strList, kvp => kvp.Key, s => s, (kvp, str) => kvp);

Categories