How get unmatched list from dictionary - c#

I want to get unmatched dictionary based on list item using LINQ. Please refer my sample code
Dictionary<string, List<int>> lowerActionRoles = new Dictionary<string, List<int>>();
Dictionary<string, List<int>> upperActionRoles = new Dictionary<string, List<int>>();
lowerActionRoles.Add("11", new List<int>() { 1, 2, 4 });
upperActionRoles.Add("11", new List<int>() { 1, 2, 4, 5 });
lowerActionRoles.Add("13", new List<int>() { 1, 2, 4 });
lowerActionRoles.Add("21", new List<int>() { 1, 2, 4 });
upperActionRoles.Add("21", new List<int>() { 1, 2, 4 });
Here I have 2 dictionary lowerActionRoles and upperActionRoles. Dictionary with key 21 matched and key 11 is not matched. Here I want get dictionary with key 11.

Based on the assumption that items in only one of the dictionaries should be ignored:
lowerActionRoles.Where(entry =>
{
if (upperActionRoles.TryGet(entry.Key, out List<int> upperActionList))
{
return !upperActionList.SequenceEqual(entry.Value);
}
else
{
return false;
}
}
This will return you a collection of entries in lowerActionRoles (IEnumerable<KeyValuePair<string, List<int>>>).
If you're interested only in the keys, add
.Select(entry => entry.Key);
to the previous query
and to convert to a new dictionary:
.ToDictionary(entry => entry.Key, entry => entry.Value);

You can test if there is key present in both of dictionaries by using ContainsKey and then for checking values of that list by using SequenceEqual like
var result = upperActionRoles
.Where(entry => lowerActionRoles.ContainsKey(entry.Key) && !lowerActionRoles[entry.Key].SequenceEqual(entry.Value))
.ToDictionary(entry => entry.Key, entry => entry.Value);
And to display your result on the console window,
foreach (var item in result)
{
Console.WriteLine("Key: " + item.Key);
Console.WriteLine();
item.Value.ForEach(x => Console.WriteLine("Value: " + x));
}
Console.ReadLine();
Output:
Note: If you want to get a list from another dictionary then just switch the dictionaries name on above query.

Related

Filter lists that are dictionary values

Dictionary<int, List<int>> foo = GetFoo();
foreach (var (key, items) in foo)
{
items = items.Where(item => item % 2 == 0); // unfortunately not in-place
foo[key] = items; // unfortunately breaks iterator
}
I have a dictionary mapping keys to lists of ints { key: [ 1, 2, 3, ... ] }
How can I filter the values of the dictionary? I want to get { key: [2, 4, ...] } for example.
Use RemoveAll, which takes a Predicate<T>:
foreach (var items in foo.Values)
{
items.RemoveAll(item => item % 2 == 0);
}
Iterate over all keys and set filtered list for the current key.
foreach (var key in foo.Keys.ToList())
{
foo[key] = foo[key].Where(item => item % 2 == 0).ToList();
}

How to filter members of more than one list with LINQ? [duplicate]

How do I select the unique elements from the list {0, 1, 2, 2, 2, 3, 4, 4, 5} so that I get {0, 1, 3, 5}, effectively removing all instances of the repeated elements {2, 4}?
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers =
from n in numbers
group n by n into nGroup
where nGroup.Count() == 1
select nGroup.Key;
// { 0, 1, 3, 5 }
var nums = new int{ 0...4,4,5};
var distinct = nums.Distinct();
make sure you're using Linq and .NET framework 3.5.
With lambda..
var all = new[] {0,1,1,2,3,4,4,4,5,6,7,8,8}.ToList();
var unique = all.GroupBy(i => i).Where(i => i.Count() == 1).Select(i=>i.Key);
C# 2.0 solution:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, int> counts = new Dictionary<T, int>();
foreach (T item in things)
{
int count;
if (counts.TryGetValue(item, out count))
counts[item] = ++count;
else
counts.Add(item, 1);
}
foreach (KeyValuePair<T, int> kvp in counts)
{
if (kvp.Value == 1)
yield return kvp.Key;
}
}
Here is another way that works if you have complex type objects in your List and want to get the unique values of a property:
var uniqueValues= myItems.Select(k => k.MyProperty)
.GroupBy(g => g)
.Where(c => c.Count() == 1)
.Select(k => k.Key)
.ToList();
Or to get distinct values:
var distinctValues = myItems.Select(p => p.MyProperty)
.Distinct()
.ToList();
If your property is also a complex type you can create a custom comparer for the Distinct(), such as Distinct(OrderComparer), where OrderComparer could look like:
public class OrderComparer : IEqualityComparer<Order>
{
public bool Equals(Order o1, Order o2)
{
return o1.OrderID == o2.OrderID;
}
public int GetHashCode(Order obj)
{
return obj.OrderID.GetHashCode();
}
}
If Linq isn't available to you because you have to support legacy code that can't be upgraded, then declare a Dictionary, where the first int is the number and the second int is the number of occurences. Loop through your List, loading up your Dictionary. When you're done, loop through your Dictionary selecting only those elements where the number of occurences is 1.
I believe Matt meant to say:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, bool> uniques = new Dictionary<T, bool>();
foreach (T item in things)
{
if (!(uniques.ContainsKey(item)))
{
uniques.Add(item, true);
}
}
return uniques.Keys;
}
There are many ways to skin a cat, but HashSet seems made for the task here.
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
HashSet<int> r = new HashSet<int>(numbers);
foreach( int i in r ) {
Console.Write( "{0} ", i );
}
The output:
0 1 2 3 4 5
Here's a solution with no LINQ:
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
// This assumes the numbers are sorted
var noRepeats = new List<int>();
int temp = numbers[0]; // Or .First() if using IEnumerable
var count = 1;
for(int i = 1; i < numbers.Length; i++) // Or foreach (var n in numbers.Skip(1)) if using IEnumerable
{
if (numbers[i] == temp) count++;
else
{
if(count == 1) noRepeats.Add(temp);
temp = numbers[i];
count = 1;
}
}
if(count == 1) noRepeats.Add(temp);
Console.WriteLine($"[{string.Join(separator: ",", values: numbers)}] -> [{string.Join(separator: ",", values: noRepeats)}]");
This prints:
[0,1,2,2,2,3,4,4,5] -> [0,1,3,5]
In .Net 2.0 I`m pretty sure about this solution:
public IEnumerable<T> Distinct<T>(IEnumerable<T> source)
{
List<T> uniques = new List<T>();
foreach (T item in source)
{
if (!uniques.Contains(item)) uniques.Add(item);
}
return uniques;
}

Convert List<int> to a Dictionary<int,int> using LINQ

Say I have the following list:
List<int> list = new List<int>() { 5, 5, 6, 6, 6, 7, 7, 7, 7 };
How could I convert the list to a Dictionary where the value is the count of each distinct number in the list by using LINQ? For example, the above list should be converted to a dictionary with the elements:
key -> value
5 -> 2
6 -> 3
7 -> 4
var result = list.GroupBy(i => i).ToDictionary(g => g.Key, g => g.Count());
Efficent solution (only 1 iteration over collection):
var output = new Dictionary<int, int>();
foreach (var val in list)
{
if (!output.ContainsKey(val))
{
output.Add(val, 1);
}
else
{
output[val] = output[val] + 1;
}
}
var group = list.GroupBy(T => T).ToDictionary(T => T.Key, T => T.Count())
try this:
var dic = list.GroupBy(c => c)
.Select(c => new {c.Key, Count = c.Count()})
.ToDictonary(c => c.Key, q => q.Count)

How to efficiently prune a list based on "is-subset" condition?

Let's suppose I am given Dictionary<int, List<int>> and I want to prune it using the following condition
an item should be removed from the dictionary if there exists an itemLarger != item in the dictionary such that item.Value.Union(new[] { item.Key }) is a subset of itemLarger.Value.Union(new[] { itemLarger.Key })
That is, each item in the dictionary will be represented by a list of numbers obtained by appending the item's key to the item's value and I want to get rid of those items which are represented by a subset of some other item's representation.
Example:
var testResult = new Dictionary<int, List<int>>
{
{ 2, new[] { 3, 4 }},
{ 3, new[] { 2, 4 }},
{ 1, new[] { 2, 3, 4 }},
{ 4, new[] { 2, 3 }}
};
In this case, the only element left in the list would be {1, {2, 3, 4}}
I can't seem to find some elegant way to do it, since
GroupBy does not allow me to specify which element in particular should be used as the key when I have two that should be groupped
Distinct does not allow me to specify, in case two elements are not distinct, which of them should be kept in the list
Of course it is doable in a trivial way. I wonder if there is some nice one.
Thank you for any ideas.
I don't think this will be much different from the "trivial" way you meant, but here's a LINQ solution:
var sets = testResult
.Select(x => new { Key = x.Key, Set = new HashSet<int>(x.Value.Concat(new[] { x.Key })) })
.ToList();
var res = sets.Where(s => sets.Any(x => x.Set.IsSupersetOf(s.Set) && x.Key != s.Key));
var keysToRemove = res.Select(x => x.Key);
This might not be the most effective way but it's short and kind of readable.
var test = new Dictionary<int, List<int>>
{
{ 2, new List<int> { 3, 4 }},
{ 3, new List<int> { 2, 4 }},
{ 1, new List<int> { 2, 3, 4 }},
{ 4, new List<int> { 2, 3 }}
};
var res = test.Where(n => !test.Any(m => m.Key!=n.Key && n.Value.Intersect(m.Value).Count()==n.Value.Count) );
In my solution I check for each element x in testResult if this element is subset of any other element in testResult. If it isn't, this element pass 'where' filter in linq expression. Two last lines are conversion of result from list representation to dictionary representation.
var listResult =
(from x in testResult
where (from y in testResult
where !x.Value.Except(y.Value).Any() && x.Key != y.Key
select y).Count() == 0
select x).ToList();
var dictionaryResult = new Dictionary<int, List<int>>();
listResult.ForEach(x => dictionaryResult.Add(x.Key, x.Value));
Edit:
We could write it even shorter:
testResult = testResult.Where(x =>
(from y in testResult
where !x.Value.Except(y.Value).Any() && x.Key != y.Key
select y).Count() == 0).ToDictionary(x => x.Key, x => x.Value);

Search for list of integers within another list C#

I would like to find all the occurances of my source list in my dictionary.
Currently, I'm looping through my dictionary, and comparing each value of dictionary.
Dictionary<string, list<int>> refList.
List<int> sourceList.
foreach(KeyValuePair<string, List<int>> kvp in refDict)
{
List<int> refList = (List<int>)kvp.Value;
bool isMatch = (refList.Count == sourceList.Count && refList.SequenceEqual(sourceList));
if (isMatch)
{
......
......
}
}
I would like to find the indexes in my dict of all the occurances of my source list.
I do not understand why you need a position (not an index!) of dictionary items because order of items is non deterministic, MSDN:
For purposes of enumeration, each item in the dictionary is treated as
a KeyValuePair structure representing a value and its
key. The order in which the items are returned is undefined.
but anyways:
Prepare data:
IDictionary<string, List<int>> refDict = new Dictionary<string, List<int>>
{
{"item1", new List<int> {1, 2, 3}},
{"item2", new List<int> {4, 5, 6}},
{"item3", new List<int> {1, 2, 3}}
};
List<int> sourceList = new List<int> {1, 2, 3};
Search for indexes:
var indexes = refDict.Values
.Select((list, index) => list.SequenceEqual(sourceList) ? index : -1)
.Where(x => x >= 0);
Search for keys:
var keys = refDict
.Where(item => item.Value.SequenceEqual(sourceList))
.Select(item => item.Key);
var list = new List<int> { 1,2,3 };
var dico = new Dictionary<string, List<int>>();
dico.Add("A", list);
dico.Add("B", new List<int> { 2,3 });
dico.Add("C", new List<int> { 1,2,3 });
var keys = dico.Where (d => d.Value.SequenceEqual(list)).Select (d => d.Key);
Pay attention that this wil return "A" and "C" !!!

Categories

Resources