LINQ to List<KeyValuePair> - c#

I'm currently working on an .NET Framework 4.7.2 application. I'm working on a LINQ query selecting objects from the given data structure:
List<KeyValuePair<int, Dictionary<string, object>>>
It is a list of dynamic objects. I need to select all elements from the list, where the value in the dictionary is true, the key would be IsDummy in this case.
The following image shows the data structure xyz in debug mode:
var result = xyz
.Select(a => a.Value)
.SelectMany(b => b)
.Where(c => c.Key == "IsDummy" && (bool)c.Value == true);
I would like to select a List<KeyValuePair<int, Dictionary<string, object>>> where the value object in the dictionary is of type boolean and has the value true.
Unfortunately my current query doesn't work correctly.
Do you know how to solve this LINQ query? It is a bit tricky because of the KeyValuePair and Dictionary within the list.
Thank you very much!

This should work:
var result = xyz
.Where(kv => kv.Value.TryGetValue("IsDummy", out object value)
&& value is bool b && b); // pattern matching C#7
Non C#7 version:
...
&& value is bool && (bool)value);

You specified:
I would like to select a List<KeyValuePair<int, Dictionary<string,
object>>> where the value object in the dictionary is of type boolean
and has the value true.
The value object? Does this mean that all Dictionaries in the list have only one Value?
Or do you only want those elements from your list that have a Dictionary that has only one value: a Boolean that is true?
Or do you only want those elements from your list that have a Dictionary with at least one Boolean value true?
Or maybe all values should be Booleans with true value?
.
List<KeyValuePair<int, Dictionary<string, object>>> source = ...
var result = source
// keep only those items in the list that have a Dictionary with only one value,
// namely a Boolean that is true
.Where(pair => pair.Value.Count == 1
// pair.Value is a Dictionary;
// pair.Value.Values are the values in the Dictionary
// only keep this item if the one and only value in the dictionary is a Boolean with a value true
&& pair.Value.Values.First().GetType() == typeof(bool)
&& (bool)pair.Value.ValuesFirst());
This can be optimized, using this you'll enumerate the dictionary twice. I leave it like this for improved readability.
2nd specification: Keep only those elements from the list that have a Dictionary that has at least one Boolean with a true value
var result = source
.Where(pair => pair.Value.Values
// keep only the values in the dictionary that are Boolean:
.OfType<bool>()
// keep only the true values
.Where(dictionaryValue => dictionaryValue)
// keep this list item if there is at least one such value in the dictionary
.Any());
With these examples, it will be easy to code the version where all items in the dictionary must be Booleans with a true value

Related

Can I use LINQ to find out if a given property in a list repeats itself?

I have a list of objects that have a name field on them.
I want to know if there's a way to tell if all the name fields are unique in the list.
I could just do two loops and iterate over the list for each value, but I wanted to know if there's a cleaner way to do this using LINQ?
I've found a few examples where they compare each item of the list to a hard coded value but in my case I want to compare the name field on each object between each other and obtain a boolean value.
A common "trick" to check for uniqueness is to compare the length of a list with duplicates removed with the length of the original list:
bool allNamesAreUnique = myList.Select(x => x.Name).Distinct().Count() == myList.Count();
Select(x => x.Name) transforms your list into a list of just the names, and
Distict() removes the duplicates.
The performance should be close to O(n), which is better than the O(n²) nested-loop solution.
Another option is to group your list by the name and check the size of those groups. This has the additional advantage of telling you which values are not unique:
var duplicates = myList.GroupBy(x => x.Name).Where(g => g.Count() > 1);
bool hasDuplicates = duplicates.Any(); // or
List<string> duplicateNames = duplicates.Select(g => g.Key).ToList();
While you can use LINQ to group or create a distinct list, and then compare item-wise with the original list, that incurs a bit of overhead you might not want, especially for a very large list. A more efficient solution would store the keys in a HashSet, which has better lookup capability, and check for duplicates in a single loop. This solution still uses a little bit of LINQ so it satisfies your requirements.
static public class ExtensionMethods
{
static public bool HasDuplicates<TItem,TKey>(this IEnumerable<TItem> source, Func<TItem,TKey> func)
{
var found = new HashSet<TKey>();
foreach (var key in source.Select(func))
{
if (found.Contains(key)) return true;
found.Add(key);
}
return false;
}
}
If you are looking for duplicates in a field named Name, use it like this:
var hasDuplicates = list.HasDuplicates( item => item.Name );
If you want case-insensitivity:
var hasDuplicates = list.HasDuplicates( item => item.Name.ToUpper() );

Search For keys Matching values in a dictionary of list in C#

I have a dictionary of lists.
var dicAclWithCommonDsEffectivity = new Dictionary<string, List<int>>();
var list1 = new List<int>(){1,2,3};
var list2 = new List<int>(){2,4,6};
var list3 = new List<int>(){3,7,6};
var list4 = new List<int>(){8,7,6};
dicAclWithCommonDsEffectivity.Add("ab",list1);
dicAclWithCommonDsEffectivity.Add("bc",list2);
dicAclWithCommonDsEffectivity.Add("cd",list3);
dicAclWithCommonDsEffectivity.Add("de",list4);
I want to get the keys in dictionary for which atleast one matching value with the current key list.
for key "ab"(first list). I should get: "ab","bc" and "cd".Since these lists contain one of the matching element in {1,2,3}
Is there a way without looping through each item in the list of dictionary value.
Is there a way without looping through each item in the list of dictionary value.
Something has to loop - dictionaries are only designed to look up by key, and you're not doing that other than for the first check.
You can do this fairly easily though:
private IEnumerable<string> GetMatchingKeys(
Dictionary<string, List<int>> dictionary, string key)
{
// TODO: Use TryGetValue if key might not be in dictionary
HashSet<int> elements = new HashSet<int>(dictionary[key]);
return dictionary.Where(pair => pair.Value.Any(x => elements.Contains(x)))
.Select(pair => pair.Key);
}
This uses the fact that Dictionary implements IEnumerable<KeyValuePair<TKey, TValue>> - so the Where clause checks a particular entry by spotting if any of the elements of its value matches any of the elements of the original value. The Select clause then projects the pair to just the key.
If you need to do this a lot and you're concerned about efficiency, another alternative would be to build a second dictionary from int to List<string> - basically a reverse mapping. You'd need to maintain that, but then you could easily fetch all the "original keys" mapping to each of the values corresponding to the given key, and just use Distinct to avoid duplicates.
It's always better to check for existence of the searchKey in the Dictionary before accessing them. Then you can get the associated list in the dictionary for that particular key.
You can also try like this:
string searchKey = "ab";
if (dicAclWithCommonDsEffectivity.ContainsKey(searchKey))
{
var ListToSearch = dicAclWithCommonDsEffectivity[searchKey];
var resultKeys = dicAclWithCommonDsEffectivity.Where(x =>
x.Value.Any(y => ListToSearch.Contains(y)))
.Select(x => x.Key)
.ToList();
}
else
{
// Specified key was not found
}
If what you mean is to visibly (not logically) remove the looping, you could use LINQ with proper Where filter to do that and Select the keys from the Dictionary which have any value element(s) intersect(s) with the selected List (List in the Dictionary with key == "ab") like this:
string key = "ab";
List<int> selectedList = dicAclWithCommonDsEffectivity[key];
var results = dicAclWithCommonDsEffectivity
.Where(x => x.Value.Any(y => selectedList.Contains(y)))
.Select(x => x.Key);
If you want to logically remove the looping too, please consider Mr. Skeet's answer.

Find Tuple item in list

I am saving multiple tuples that contain strings in a list:
List<Tuple<string, string, string>> ExcelRawImport
What i want to do now, is find the tuple in the list, where the Tuple.Item1 has an specific value.
how should i do this ?
With Linq I would say
var specificValue = "mySpecificValue";
var foundTuple = ExcelRawImport.FirstOrDefault(eri => eri.Item1 == specificValue);
This returns the first Item from your list that matches the specific value. If no matching value is found, foundTuple is null.
If you need all items that match you can use the Where() statement instead.
var foundTuples = ExcelRawImport.Where(eri => eri.Item1 == specificValue).ToList();

Remove all items that match a condition in IDictionary

I'm trying to remove all elements in a IDictionary object that match a condition.
E.g. the IDictionary contains a set of keys and corresponding values (let say 80 objects). The keys are strings, the values could be of different types (think extracting metadata from a wtv file using directshow).
Some of the keys contains the text "thumb", e.g. thumbsize, startthumbdate etc.
I want to remove all objects from the IDictionary who's keys contain the word thumb.
The only way I'm seeing here is to manually specify each key name using the .Remove method.
Is there some way to get all the objects who's keys contain the word thumb and them remove them from the IDictionary object.
The code looks like this:
IDictionary sourceAttrs = editor.GetAttributes();
GetAttributes is defined as:
public abstract IDictionary GetAttributes();
I don't have control over GetAttributes, it's returns an IDictionary object, I only know the contents by looking at it while debugging. (likely a HashTable)
UPDATE: Final Answer thanks to Tim:
sourceAttrs = sourceAttrs.Keys.Cast<string>()
.Where(key => key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
.ToDictionary(key => key, key => sourceAttrs[key]);
So you want to remove all entries where the key contains a sub-string.
You can use LINQ by keeping all that does not contain it:
dict = dict
.Where(kv => !kv.Key.Contains("thumb"))
.ToDictionary(kv => kv.Key, kv => kv.Value);
If you want a case-insensitive comparison you can use IndexOf:
dict = dict
.Where(kv => kv.Key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
.ToDictionary(kv => kv.Key, kv => kv.Value);
Update according to your non-generic edit:
If it's a non-generic dictionary like a HashTable you cannot use LINQ directly, but if you know that the key is a string you could use following query:
// sample IDictionary with an old Hashtable
System.Collections.IDictionary sourceAttrs = new System.Collections.Hashtable
{
{"athumB", "foo1"},
{"other", "foo2"}
};
Dictionary<string, object> newGenericDict = sourceAttrs.Keys.Cast<string>()
.Where(key => !key.Contains("thumb"))
.ToDictionary(key => key, key => sourceAttrs[key]);
But maybe it's actually a generic Dictionary, you can try-cast with the as operator:
var dict = sourceAttrs as Dictionary<string, object>;
It's null if the cast didn't work.
If your Dictionary is read only, you will need to remove the items one by one, in which case you can also use LINQ:
dict
.Keys
.Where(p => p.Contains("thumb"))
.ToList
.ForEach(p => dict.Remove(p);
Note this works because at removal you are not looping through the dictionary anymore: you first loop entirely through the dictionary to build a list of keys to delete, then you loop through this list and re-access the dictionary to remove the keys one by one.
If your dictionary is not read only and efficiency is a concern, you are better off with Tim's suggestions.

Return number of matches from c# dictionary

I have a dictionary with non unique values and I want to count the matches of a string versus the values.
Basically I now do dict.ContainsValue(a) to get a bool telling me if the string a exists in dict, but I want to know not only if it exists but how many times it exists (and maybee even get a list of the keys it exists bound to)
Is there a way to do this using dictionary, or should I look for a different collection?
/Rickard Haake
To get the number of instances of the value you could do something like this:
dict.Values.Count(v => v == a);
To find the keys that have this value you could do this:
dict.Where(kv => kv.Value == a).Select(kv => kv.Key);
To get the count use Values.Count:
int count = dict.Values.Count(x => x == "foo");
To get the keys I prefer the query syntax:
var keys = from kvp in dict
where kvp.Value == "foo"
select kvp.Key;
Note that this will require scanning the entire dictionary. For small dictionaries or infrequent lookups this may not be a problem.
If you are making many lookups you may wish to maintain a second dictionary that maps the values to the keys. Whilst this will speed up lookups, it will slow down modifications as both dictionaries will need updating for each change.
what about using LINQ: if a is the value you're looking for, the the code could be
dict.Values.Where(v => v == a).Count();

Categories