I have a method that receives a list of Foo object. a Foo object contains a property that is a Dictionary<string,object>. (where object will be a primitive type: string, int, double...) I am trying to get the Foo objects in the list whose dictionary contains an element with a given value.
What I tried:
public List<Foo> FindFoos(List<Foo> foos, string itemKey, object itemValue) {
List<Foo> found = foos.Where(f => f.TheDict[itemKey] == itemValue).ToList();
return found;
}
and then call it with:
var a = FindFoos(foos, "name", "Something");
var b = FindFoos(foos, "age", 45);
I have a method that receives a list of Foo object. a Foo object contains a property that is a Dictionary<string,object>. (where object will be a primitive type: string, int, double...) I am trying to get the Foo objects in the list whose dictionary contains an element with a given value.
What I tried:
public List<Foo> FindFoos(List<Foo> foos, string itemKey, object itemValue) {
List<Foo> found = foos.Where(f => f.TheDict[itemKey] == itemValue).ToList();
return found;
}
and then call it with:
var a = FindFoos(foos, "name", "Something");
var b = FindFoos(foos, "age", 45);
Share
Improve this question
asked Mar 11 at 16:24
patsy2kpatsy2k
6113 silver badges11 bronze badges
2
- 3 So what's the problem? – Blindy Commented Mar 11 at 16:56
- 1 Please edit the question to include a minimal reproducible example rather than snippets. – Jon Skeet Commented Mar 11 at 17:02
3 Answers
Reset to default 1Maybe this:
public List<Foo> FindFoos(List<Foo> foos, string itemKey, object itemValue)
{
List<Foo> found = foos
.Where(f => f.TheDict.TryGetValue(itemKey, out var someValue)
&& someValue.Equals(itemValue))
.ToList();
return found;
}
==
does a reference equality on object
. Use Object.Equals
instead. This will use the overridden implementations of the specific object types.
public List<Foo> FindFoos(List<Foo> foos, string itemKey, object itemValue) {
return foos
.Where(f => f.TheDict[itemKey].Equals(itemValue))
.ToList();
}
This will work well if only valid item keys are provided. If you have keys that are not in the dictionary, use Ivan Petrov's approach with TryGetValue
.
I wanted to build on Ivan's excellent answer ▲ by suggesting that you write this as an extension method so that it can be used and chained just like any other Linq-Like expression.
Extension
public static class Extensions
{
public static List<Foo> FindFoos(this List<Foo> foos, string itemKey, object itemValue) =>
(string.IsNullOrWhiteSpace(itemKey)
? new List<Foo>()
: foos
.Where(_ => _.TheDict
.TryGetValue(itemKey, out var someValue) &&
someValue.Equals(itemValue)))
.ToList();
}
Usage
// List<Foo> superset is some initialized list of Foo.
List<Foo> subset = superset.FindFoos("Occupation", "Engineer"))
Minimal example on Console App
Console.Title = "Test Extension";
List<Foo> sourcelist =
Enumerable.Range(0, 4)
.Select(n => new Foo {Id = n}.WithTestData())
.ToList();
foreach (var foo in sourcelist.FindFoos("Occupation", "Engineer"))
{
Console.WriteLine($"Match found in Foo #{foo.Id}");
}
Console.ReadKey();
return;
public class Foo
{
public int Id { get; init; }
public Dictionary<string, object> TheDict { get; } = new();
public override string ToString() => $"{Id}";
}
public static class Extensions
{
public static List<Foo> FindFoos(this List<Foo> foos, string itemKey, object itemValue) =>
(string.IsNullOrWhiteSpace(itemKey)
? new List<Foo>()
: foos
.Where(_ => _.TheDict
.TryGetValue(itemKey, out var someValue) &&
someValue.Equals(itemValue)))
.ToList();
public static Foo WithTestData(this Foo @this)
{
switch (@this.Id)
{
case 0:
@this.TheDict.Add("Name", "Alice");
@this.TheDict.Add("Age", 25);
@this.TheDict.Add("Occupation", "Engineer");
break;
case 1:
@this.TheDict.Add("Name", "Bob");
@this.TheDict.Add("Age", 30);
@this.TheDict.Add("Occupation", "Engineer"); // Same occupation as Alice
break;
case 2:
@this.TheDict.Add("Name", "Charlie");
@this.TheDict.Add("Age", 35);
@this.TheDict.Add("Occupation", "Artist");
break;
case 3:
@this.TheDict.Add("Name", "Diana");
@this.TheDict.Add("Age", 28);
@this.TheDict.Add("Occupation", "Chef");
break;
}
return @this;
}
}