我有一组对象(让我们称之为MyItem
),每个MyItem
都有一个名为IsCompatibleWith
的方法,它返回一个布尔值,表示它是否与另一个MyItem
兼容
public class MyItem
{
...
public bool IsCompatibleWith(MyItem other) { ... }
...
}
A.IsCompatibleWith(B)
将始终与B.IsCompatibleWith(A)
相同。例如,如果我有一个包含其中4个的集合,我试图找到一个LINQ查询,该查询将在同一集合中的每个不同的项目对上运行该方法。因此,如果我的集合包含A,B,C和D,我希望做相同的:
A.IsCompatibleWith(B); // A & B
A.IsCompatibleWith(C); // A & C
A.IsCompatibleWith(D); // A & D
B.IsCompatibleWith(C); // B & C
B.IsCompatibleWith(D); // B & D
C.IsCompatibleWith(D); // C & D
最初使用的代码是:
var result = from item in myItems
from other in myItems
where item != other &&
item.IsCompatibleWith(other)
select item;
但当然这仍然会同时执行A & B
和B & A
(这不是必需的且效率不高)。此外,值得注意的是,实际上这些列表将比4个项目大很多,因此需要最佳解决方案。
希望这有意义......任何想法?
编辑: 一个可能的查询 -
MyItem[] items = myItems.ToArray();
bool compatible = (from item in items
from other in items
where
Array.IndexOf(items, item) < Array.IndexOf(items, other) &&
!item.IsCompatibleWith(other)
select item).FirstOrDefault() == null;
Edit2:最后切换到使用LukeH的自定义解决方案,因为它对更大的列表更有效。
public bool AreAllCompatible()
{
using (var e = myItems.GetEnumerator())
{
var buffer = new List<MyItem>();
while (e.MoveNext())
{
if (buffer.Any(item => !item.IsCompatibleWith(e.Current)))
return false;
buffer.Add(e.Current);
}
}
return true;
}
答案 0 :(得分:3)
修改... 强>
根据您的问题中添加的“最终查询”判断,您需要一种方法来确定集合中的所有项目是否彼此兼容。以下是如何合理有效地完成这项工作:
bool compatible = myItems.AreAllItemsCompatible();
// ...
public static bool AreAllItemsCompatible(this IEnumerable<MyItem> source)
{
using (var e = source.GetEnumerator())
{
var buffer = new List<MyItem>();
while (e.MoveNext())
{
foreach (MyItem item in buffer)
{
if (!item.IsCompatibleWith(e.Current))
return false;
}
buffer.Add(e.Current);
}
}
return true;
}
原始答案......
我认为只使用内置的LINQ方法有效的方法。
虽然很容易建立自己的。这是您需要的代码类型的示例。我不确定完全你想要返回什么结果,所以我只是在控制台上为每个兼容的对写一条消息。它应该很容易改变它以产生你需要的结果。
using (var e = myItems.GetEnumerator())
{
var buffer = new List<MyItem>();
while (e.MoveNext())
{
foreach (MyItem item in buffer)
{
if (item.IsCompatibleWith(e.Current))
{
Console.WriteLine(item + " is compatible with " + e.Current);
}
}
buffer.Add(e.Current);
}
}
(请注意,虽然这是相当有效的,但它不保留集合的原始顺序。这是您的情况中的问题吗?)
答案 1 :(得分:1)
这应该这样做:
var result = from item in myItems
from other in myItems
where item != other &&
myItems.indexOf(item) < myItems.indexOf(other) &&
item.IsCompatibleWith(other)
select item;
但我不知道它是否会使它更快,因为在查询中必须检查每行的行索引。
编辑: 如果myItem中有索引,则应使用该索引而不是indexOf。你可以从where子句中删除“item!= other”,现在有点多余
答案 2 :(得分:0)
这是一个想法:
实施IComparable
以便MyItem
可以排序,然后运行此linq查询:
var result = from item in myItems
from other in myItems
where item.CompareTo(other) < 0 &&
item.IsCompatibleWith(other)
select item;
答案 3 :(得分:0)
如果你的MyItem集合足够小,你可以将item.IsCompatibleWith(otherItem)
的结果存储在一个布尔数组中:
var itemCount = myItems.Count();
var compatibilityTable = new bool[itemCount, itemCount];
var itemsToCompare = new List<MyItem>();
var i = 0;
var j = 0;
foreach (var item in myItems)
{
j = 0;
foreach (var other in itemsToCompare)
{
compatibilityTable[i,j] = item.IsCompatibleWith(other);
compatibilityTable[j,i] = compatibilityTable[i,j];
j++;
}
itemsToCompare.Add(item);
i++;
}
var result = myItems.Where((item, i) =>
{
var compatible = true;
var j = 0;
while (compatible && j < itemCount)
{
compatible = compatibilityTable[i,j];
}
j++;
return compatible;
}
答案 4 :(得分:0)
所以,我们有
IEnumerable<MyItem> MyItems;
要获得所有组合,我们可以使用这样的函数。
//returns all the k sized combinations from a list
public static IEnumerable<IEnumerable<T>> Combinations<T>(IEnumerable<T> list,
int k)
{
if (k == 0) return new[] {new T[0]};
return list.SelectMany((l, i) =>
Combinations(list.Skip(i + 1), k - 1).Select(c => (new[] {l}).Concat(c))
);
}
然后我们可以将此功能应用于我们的问题。
var combinations = Combinations(MyItems, 2).Select(c => c.ToList<MyItem>());
var result = combinations.Where(c => c[0].IsCompatibleWith(c[1]))
这将对所有组合执行IsCompatableWith
而不重复。
您当然可以在Combinations
函数中执行检查。对于进一步的工作,您可以将Combinations
函数放入一个扩展中,该扩展使一个具有可变数量参数的委托用于多个k
长度。
编辑:正如我上面所建议的,如果你写了这些扩展方法
public static class Extenesions
{
IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> list, int k)
{
if (k == 0) return new[] { new T[0] };
return list.SelectMany((l, i) =>
list.Skip(i + 1).Combinations<T>(k - 1)
.Select(c => (new[] { l }).Concat(c)));
}
IEnumerable<Tuple<T, T>> Combinations<T> (this IEnumerable<T> list,
Func<T, T, bool> filter)
{
return list.Combinations(2).Where(c =>
filter(c.First(), c.Last())).Select(c =>
Tuple.Create<T, T>(c.First(), c.Last()));
}
}
然后在你的代码中你可以做更优雅(IMO)
var compatibleTuples = myItems.Combinations(a, b) => a.IsCompatibleWith(b)))
然后使用
获取兼容的项目foreach(var t in compatibleTuples)
{
t.Item1 // or T.item2
}