问题
I have a list of objects. These objects have a property e.g. "Value".
var lst = new List<TestNode>();
var c1 = new TestNode()
{
Value = "A",
};
lst.Add(c1);
var c2 = new TestNode()
{
Value = "A",
};
lst.Add(c2);
var c3 = new TestNode()
{
Value = "B",
};
lst.Add(c3);
var c4 = new TestNode()
{
Value = "B",
};
lst.Add(c4);
I would like to say something like:
lst.PartialDistinct(x => x.Value == "A")
This should only be distinct by predicate and when printing the "Value"s of the resulting IEnumerable the result should be:
A
B
B
I already found solutions for DistinctBy where it's possible to define a Key-Selector. But the result is then of course:
A
B
Cyral's first answer did the job. So I accepted it. But Scott's answer is really a PartialDistinct() Method as asked and looks like it solves all my problems.
Ok, thought it's solved with Scott's solution but it's not. Maybe I made a mistake while unit testing... or I don't know. The problem is:
if(seen.Add(item))
This does not filter out other objects with value "A".I think this is because it relies on referential equality when putting into hashset.
I ended up with following solution:
public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source Func<T, bool> predicate)
{
return source
.Where(predicate)
.Take(1)
.Concat(source.Where(x => !predicate(x)));
}
回答1:
Try finding the elements that match your value, taking the first element of that, and then joining that with all that don't match your value.
var match = "A";
var result = lst
.Where(x => x.Value == match)
.Take(1)
.Concat(lst
.Where(x => x.Value != match))
.Select(x => x.Value);
回答2:
You can group by Value
and perform 'conditional flattening' using SelectMany
, i.e. take only one element from 'A' groups and all elements from the rest of the groups:
var result = lst.GroupBy(x => x.Value)
.SelectMany(g => g.Key == "A" ? g.Take(1) : g);
回答3:
If you are wanting to distinct on TestNode
where Value == A
plus the normal result set where Value != A
just do that exact process and wrap it up in to a extension method.
public static class ExtensionMethods
{
public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source, Func<T, bool> filter)
{
return PartialDistinct(source, filter, EqualityComparer<T>.Default);
}
public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source, Func<T, bool> filter, IEqualityComparer<T> comparer)
{
HashSet<T> seen = new HashSet<T>(comparer);
foreach (var item in source)
{
if (filter(item))
{
if (seen.Add(item))
{
yield return item;
}
}
else
{
yield return item;
}
}
}
回答4:
Creating two different list and concatenating them is also a way.
var answer = lst.Where(val=>val == "A").Select(note=>note.Value).Distinct().Concat(
lst.Where(node => node.Value == "B").Select(node => node.Value));
来源:https://stackoverflow.com/questions/28618080/partial-distinct-linq