Reputation: 273
A seemingly simple problem: I have a list of strings (read from a property file):
IList<string> defaultValues = new List<string> {"0458","0309"};
and another list of some Objects, each having a property of type string:
IList<Token> tokens = new List<Token>
{
new Token {DisplayValue = "0123"},
new Token {DisplayValue = "0309"},
new Token {DisplayValue = "0203"},
new Token {DisplayValue = "0458"},
new Token {DisplayValue = "0911"}
};
public class Token
{
public string DisplayValue { get; set; }
}
Now I would like to get the element of tokens where DisplayValue matches the first element of defaultValues (0485). If no element with DisplayValue 0485 is found, the second element in defaultvalues should match (0309) and so on.
The defaultValues List can be dynamic, so more values could be added, and always the first entry should have priority.
So the defaultValues list is sort of a priority list of strings, the lower the index the higher the priority. In the example above, the result should be "0458".
I could do something like this:
string result = string.Empty;
foreach (var searchValue in defaultValues)
{
if (tokens.Any(token => token.DisplayValue == searchValue))
{
result = searchValue;
}
}
But I think something like this could be done more elegantly and with no foreach ...
Upvotes: 1
Views: 508
Reputation: 3892
Using Any
is a O(n^2) in this case, which is probably not the most performant. Probably the best way to do this would be to do a GroupJoin
(making it O(n) instead) and then select the first token that was matched. Just keep in mind that GroupJoin
only does it on exactly matching keys, so if you are looking for a comparison or substring search, you'll have to find other ways.
defaultValues
.GroupJoin(
tokens, // matching 0:n tokens per default value
defVals => defVals, // key selector for our left source
tks => tks.DisplayValue, // key selector for our right source
(defVal, tks) => tks.FirstOrDefault()) // result selector for our matches
.FirstOrDefault(match => match != null)
This has the advantage of only traversing each collection once, and matches all tokens with the corresponding DefaultValue
(so you could have multiple matches per default value). You COULD do defaultValues.FirstOrDefault(x => tokens.Any(t => t == x))
, but you'd run into the same problem (with possible O(n^2) complexity). From here, you can just check whether it's null (use null coalescing) and add a static constant called Empty
in Token that initializes to new Token { DisplayValue = string.Empty }
. After that you can do this:
(defaultValues
.GroupJoin(
...
.FirstOrDefault(match => match != null) ?? Token.Empty).DisplayValue
Upvotes: 2
Reputation: 5843
Basically you need to look for a projection from your Linq/Lambada result.
I would try something like this:
var macthes = tokens.Any(t => t.DisplayValue.In(defaultValues))
.Select(y =>y.DisplayValue);
Upvotes: 0