[英]Linq get values not shared across multiple lists
編寫一個比較n個列表並返回所有未出現在所有列表中的值的方法的最有效方法是什么,以便
var lists = new List<List<int>> {
new List<int> { 1, 2, 3, 4 },
new List<int> { 2, 3, 4, 5, 8 },
new List<int> { 2, 3, 4, 5, 9, 9 },
new List<int> { 2, 3, 3, 4, 9, 10 }
};
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
//...fast algorithm here
}
以便
lists.GetNonShared();
返回1,5,8,9,10
我有
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(item => item)
.Except(lists.Aggregate((a, b) => a.Intersect(b));
}
但我不確定這是否有效。 訂單無關緊要。 謝謝!
public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list)
{
return list.SelectMany(x => x.Distinct()).GroupBy(x => x).Where(g => g.Count() < list.Count()).Select(group => group.Key);
}
編輯:我想我會這樣想的......
您想要所有列表的並集 ,減去所有列表的交集 。 這實際上就是你的原始版本所做的事情, Except
得到重復的輸入,否則不要做Union
的“設置”操作。 在這種情況下,我懷疑你可以更有效地做到這一點,只需構建兩個HashSet
並就地完成所有工作:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
using (var iterator = lists.GetEnumerator())
{
if (!iterator.MoveNext())
{
return new T[0]; // Empty
}
HashSet<T> union = new HashSet<T>(iterator.Current.ToList());
HashSet<T> intersection = new HashSet<T>(union);
while (iterator.MoveNext())
{
// This avoids iterating over it twice; it may not be necessary,
// it depends on how you use it.
List<T> list = iterator.Current.Toist();
union.UnionWith(list);
intersection = intersection.IntersectWith(list);
}
union.ExceptWith(intersection);
return union;
}
}
請注意,現在這是急切的, 而不是延遲的。
這是另一種選擇:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(list => list)
.GroupBy(x => x)
.Where(group => group.Count() < lists.Count)
.Select(group => group.Key);
}
如果列表可能不止一次包含相同的項目,那么您需要在其中進行不同的調用:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(list => list.Distinct())
.GroupBy(x => x)
.Where(group => group.Count() < list.Count)
.Select(group => group.Key);
}
編輯:現在我已經糾正了這一點,我理解你的原始代碼......我懷疑我能找到更好的東西...思考......
public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list)
{
var lstCnt=list.Count(); //get the total number if items in the list
return list.SelectMany (l => l.Distinct())
.GroupBy (l => l)
.Select (l => new{n=l.Key, c=l.Count()})
.Where (l => l.c<lstCnt)
.Select (l => l.n)
.OrderBy (l => l) //can be commented
;
}
//對.net> = 4.5使用HashSet和SymmetricExceptWith
我認為您需要創建一個中間步驟,即找到所有列表共有的所有項目。 這對於設置邏輯很容易 - 它只是第一個列表中與每個后續列表中的項集相交的項集。 不過,我不認為LINQ中的這一步是可行的。
class Program
{
static void Main(string[] args)
{
IEnumerable<IEnumerable<int>> lists = new List<IEnumerable<int>> {
new List<int> { 1, 2, 3, 4 },
new List<int> { 2, 3, 4, 5, 8 },
new List<int> { 2, 3, 4, 5, 9, 9 },
new List<int> { 2, 3, 3, 4, 9, 10 }
};
Console.WriteLine(string.Join(", ", GetNonShared(lists)
.Distinct()
.OrderBy(x => x)
.Select(x => x.ToString())
.ToArray()));
Console.ReadKey();
}
public static HashSet<T> GetShared<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> result = null;
foreach (IEnumerable<T> list in lists)
{
result = (result == null)
? new HashSet<T>(list)
: new HashSet<T>(result.Intersect(list));
}
return result;
}
public static IEnumerable<T> GetNonShared<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> shared = GetShared(lists);
return lists.SelectMany(x => x).Where(x => !shared.Contains(x));
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.