I have a Dictionary and another List. What I am trying to achieve is a LINQ query to get all items out of the dictionary where any values from said dictionary are not in the List<string>.
I found this post to be helpful, Linq Query Dictionary where value in List. And was able to write the following LINQ expression.
What I have so far:
Data is the dictionary and PersonList is the list of strings.
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
However, my results never actually return anything.
Update:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Is it possible that you made a typo here and meant to write this instead?
// changed to kvp.Value from kvp.Key
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
This searches the values from the dictionary in the list and retains those entries which are not in the list, as per your question.
Answering this myself as I've been looking into the problem from wrong angle.
Following works as expected:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
List<string> PersonListNotInDictionary = PersonList.Where(pl => !Data.ContainsKey(pl))
.ToList();
It is possible that the contains doesn't work, because the items in the PersonList are other objects than the keys in your dictionary. The objects might be the same (have the same content), but if it are different object (different refences), than the contains will return false.
An example:
myObject = new myObject() { Id = 1 };
List<myObject> listOfObjects = new List<myObject>();
listOfObjects.Add(new myObject() { Id = 1 });
var result = listOfObjects.Contains(myObject); // returns false, because the item in the list is a different object than myObject
result = listOfObjects.Any(obj => obj.Id == myObject.Id); // returns true
I don't know what the PersonList consist of, but if the elements have, for instance, an Id property than you could do something like this:
var Persons = Data.Where(kvp => !PersonList.Any(person => person.Id == kvp.Key.Id))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can try following code snippet
var result = data.Where(d => PersonList.All(p => p != d.Value));
Related
This is my source type => IEnumerable<IDictionary<string, IEnumerable<string>>>
This is my target type => IEnumerable<string>
Expected Output
List of strings
[
"ca:aws_client:firstElementIntheList]",
"ca:aws_client:secondElementInTheList]"
]
Actual Output
List of strings
[
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]",
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]"
]
Code
input
.ToList()
.SelectMany(d => d)
.Select(i => $"ca:{i.Key}:{i.Value.Select(l => l)}")
.ToList()
You're looking to use a result selector in SelectMany instead of a second select statement.
Something like this may be what you want:
var dict = new Dictionary<string, IEnumerable<string>>
{
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
};
var res = dict.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
/* output:
ca:one:1
ca:one:2
ca:one:3
ca:two:1
ca:two:2
ca:two:3
*/
Edit:
I've noticed you're actually using a List of Dictionaries. The solution is largely the same, but with more SelectMany.
var list = new List<Dictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>> {
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
},
new Dictionary<string, IEnumerable<string>> {
{"three", new List<string> {"1","2","3"}},
{"four", new List<string> {"1","2","3"}}
}
};
var res = list.SelectMany(x => x)
.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
If someone can suggest a clean way to handle the multiple method syntax SelectMany other than stacking them, I'd like to know for my own edification.
I have two Dictionary.
Dictionary<string, string> testDict = new Dictionary<string, string>();
testDict.Add("Name", "John");
testDict.Add("City", "NY");
Dictionary<string, string> DictA = new Dictionary<string, string>();
DictA.Add("Name", "Sarah");
DictA.Add("State", "ON");
I wish to get a Dictionary such that the keys of testDict are present and the values of those keys present in DictA are present.
So the example merged dictionary should look as below:
Dictionary<string, string> DictMerged = new Dictionary<string, string>();
DictMerged.Add("Name", "Sarah");
DictMerged.Add("City", "NY");
I hope I have been able to explain my requirements..
I tried..
testDict.Concat(DictA)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value)
.ToDictionary(g => g.Key, g => g.Last());
But this gave me DictA 'State' as well which I do not want..
Any help is sincerely appreciated
Thanks
I think you are looking for this:
var result =
testDict.ToDictionary(
i => i.Key,
i => DictA.ContainsKey(i.Key) ? DictA[i.Key] : i.Value);
// result:
// {"Name", "Sarah"}
// {"City", "NY"}
If you have the GetOrDefault extension method defined here
Then you can do
var result = testDict.ToDictionary(
kvp => kvp.Key,
kvp => DictA.GetOrDefault(kvp.Key, kvp.Value));
The difference between this and using DictA.ContainsKey(kvp.Key) ? DictA[kvp.Key] : i.Value is that there is only one lookup done on DictA versus two when the key is present.
This would work
var testDict = new Dictionary<string, string> { { "Name", "John" }, { "City", "NY" } };
var DictA = new Dictionary<string, string> { { "Name", "Sarah" }, { "State", "ON" } };
var mergedDict = testDict.ToDictionary(keyVal => keyVal.Key, keyVal => DictA.ContainsKey(keyVal.Key) ? DictA[keyVal.Key] : keyVal.Value);
I have two dictionaries
One is nested dictionary -
Dictionary<string, List<string>> Dict = new Dictionary<string, List<string>>();
And another is normal one –
Dictionary<string, string> ObjDict = new Dictionary<string, string>();
In normal dictionary I have values like this
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml, 0000000047510D9744C9A54EB11C2]}
Now I want the nested dictionary like this –
“Key0” DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml
“Key1” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml
“Key2” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml
All the keys of equal values of normal dictionary should belongs to one key of nested dictionary.
Please suggest.
ObjDict.GroupBy(kvp => kvp.Value).ToDictionary(g => g.Key, g => g.Select(kvp => kvp.Key).ToList())
Ok, if I understand the question correctly:
Given you have an input dictionary where you want to group the items by the values in the dictionary and put them into a new dictionary where each key is one of the values from the original dictionary, and each value is a list of the keys with that value:
var items = new Dictionary<string, string>
{
{"C03", "C"},
{"B01", "B"},
{"A01", "A"},
{"C02", "C"},
{"A03", "A"},
{"B03", "B"},
{"B02", "B"},
{"A02", "A"},
{"C01", "C"}
};
var result = items.GroupBy(item => item.Value)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).ToList()
);
foreach (var item in result)
{
Console.Write("Values for key " + item.Key + ": ");
foreach (var value in item.Value)
Console.Write(value + " ");
Console.WriteLine();
}
The above code produces the following output:
Values for key C: C03 C02 C01
Values for key B: B01 B03 B02
Values for key A: A01 A03 A02
As you can see, this output is not ordered (it's in the same order as the input).
If you want to order both the keys in the dictionary, and the values in each list, you can do so like this:
var result = items.GroupBy(item => item.Value)
.OrderBy(item => item.Key)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).OrderBy(x => x).ToList()
);
This change produces this output:
Values for key A: A01 A02 A03
Values for key B: B01 B02 B03
Values for key C: C01 C02 C03
you can use that:
foreach (var item in ObjDict)
{
if (Dict.ContainsKey(item.Value))
{
var e = Dict[item.Value];
e.Add(item.Key);
}
else
{
Dict.Add(item.Value, new List<string>() { item.Key });
}
}
you could create your own Dictionary, MultiDictionary,
in the Multipledictionary, maintain a private dictionary with key and List
when add the value, check if key has already exists, if exists, add to the list
and you can make it Generic
public class MultipleDictionary<TKey, TValue>
{
private IDictionary<TKey, IList<TValue>> _dict;
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public TValue this[TKey key]
{
get
{
if (_dict.ContainsKey(key))
return _dict[key][0];
return default(TValue);
}
set
{
if (_dict.ContainsKey(key))
{
IList<TValue> valList = _dict[key];
valList.Add(value);
}
else
{
IList<TValue> list = new List<TValue>();
list.Add(value);
_dict.Add(key, list);
}
}
}
}
this is the idea, try implement the rest yourself
A bit of pseudocode, so you can do some work on your own :P
step 1:
make a hashTable or something which stores your ObjDict.Values and your Dict.Keys
( "0000000047510D9744C9A54EB11C0" -> "Key0" )
step2:
for each ObjDict.Key
if(Dict.contains(HashTable[thisKey])
Take List from Dict and add the ObjDict.Value
else
Make new list, add ObjDict.Value
Dict.Add(HashTable[thiskey], theList
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);
I have the following code :
using System.Collections.Generic;
public class Test
{
static void Main()
{
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User {FirstName = "Name1"}),
new KeyValuePair<int, User>(1, new User {FirstName = "Name2"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name3"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name4"})
};
}
}
public class User
{
public string FirstName { get; set; }
}
Above as you can see there are multiple users for same key . Now I want to group them and convert the list object to dictionary in which the Key will be the same(1,2 as shown above) but the value will be the collection.Like this:
var outputNeed = new Dictionary<int, Collection<User>>();
//Output:
//1,Collection<User>
//2,Collection<User>
i.e they are grouped now.
How can I achieve that ?
I would suggest you use a Lookup<TKey, TElement> instead. This data-structure is specifically intended for use as a map from keys to collections of values.
//uses Enumerable.ToLookup: the Id is the key, and the User object the value
var outputNeeded = items.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
Of course, if you do need the dictionary (to allow for mutability perhaps), you can do something like:
var outputNeeded = new Dictionary<int, Collection<User>>();
foreach(var kvp in list)
{
Collection<User> userBucketForId;
if(!outputNeeded.TryGetValue(kvp.Key, out userBucketForId))
{
// bucket doesn't exist, create a new bucket for the Id, containing the user
outputNeeded.Add(kvp.Key, new Collection<User> { kvp.Value });
}
else
{ // bucket already exists, append user to it.
userBucketForId.Add(kvp.Value);
}
}
On another note, theCollection<T>class is not all that useful unless you intend to subclass it. Are you sure you don't just need aList<User>?
Here's an example using LINQ's ToDictionary:
var output = items.GroupBy(kvp => kvp.Key)
.ToDictionary(group => group.Key,
group => group.Select(kvp => kvp.Value).ToList());
It results in a Dictionary<int,List<User>>.
Given your initial variable "item", and the suggested output variable "outputNeed", this is what you need to do:
NOTE:This is not actual c#/vb code, so please change this pseudo as required (I don't have a VS Studio at hand at the moment):
foreach (KeyValuePair<int, User> pair in items)
{
//add a new collection if this is the first time you encounter the key
if (! outputNeed.Contains(pair.Key)
{
outputNeed[pair.Key] = new ArrayList<User>());
}
//add the user to the matching collection
outputNeed.Add(pair.Key, pair.User);
}
Good luck
foreach (var u in items)
{
if (outputNeed.Contains(u.Key)) {
outputNeed[u.Key].Add(u.Value);
}
else {
Collection<User> a=new Collection<User>();
a.Add(u.Value);
outputNeed.Add(u.Key,a);
}
}
Here is my solution :
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User { FirstName = "Name1" }),
new KeyValuePair<int, User>(1, new User { FirstName = "Name2" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name3" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name4" })
};
var result = (
from item in items
group item.Value by item.Key into grouped
select grouped
).ToDictionary(g => g.Key, g => g);
DotNetFiddle