在HashSet中迭代两个元素的每个组合

时间:2014-04-23 10:15:21

标签: c# .net linq hashset

我怎样才能在HashSet中迭代两个元素的每个组合?

foreach (var elt1 in hashSet) {
  foreach (var elt2 in hashSet) {
    ...
  }
}

这会迭代两个组合,但会迭代每个组合TWICE。我想做一次。

我认为在Python中很容易做到。有没有办法在C#中做到这一点?

样品:

输入hashSet:{1,2,3,4}

遍历:(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)

4 个答案:

答案 0 :(得分:3)

在C#中没有内置方法可以做到这一点。由于HashSet<T>未编入索引 * ,因此您无法使用两个循环执行此操作。

如果这是一次性交易,最简单的解决方案是对ToList()ToArray()的结果进行两次嵌套循环,如下所示:

var items = hashSet.ToList();
for (var i = 0 ; i != items.Count ; i++) {
    var a = items[i];
    for (var j = i+1 ; j != items.Count ; j++) {
        var b = items[i];
    }
}

如果您正在寻找可重复使用的内容,请在生成所有对的IEnumerable<T>上创建一个扩展方法:

static IEnumerable<Tuple<T,T>> MakeAllPairs<T>(this IEnumerable<T> data) {
    var items = data.ToList();
    for (var i = 0 ; i != items.Count ; i++) {
        var a = items[i];
        for (var j = i+1 ; j != items.Count ; j++) {
            var b = items[i];
            yield return Tuple.Create(a, b);
        }
    }
}

现在您可以在一个循环中迭代您的对:

foreach (var pair in hashSet.MakeAllPairs()) {
    Console.WriteLine("{0} {1}", pair.Item1, pair.Item2);
}

* 从技术上讲,您可以使用ElementAt<T>(int)中的Enumerable扩展名,但对于大型集合,这会非常慢。

答案 1 :(得分:2)

我最初误解了这个问题。这是一个新答案

这就是你想要的(如果基于索引的工作是一个选项)。说明如下

string[] myArray = GetArray();

for (int i = 0; i < myArray.Length - 1; i++)
{
    var element1 = myArray[i];

    for(int j = i + 1; j < myArray.Length; j++)
    {
        var element2 = myArray[j];
        Console.WriteLine("{0} {1}", element1, element2);
    }
}

说明:假设以下数组:

Apple, Banana, Coconut, Zucchini

i = 0(Apple),j为1(香蕉),然后是2(椰子),然后是3(Zucchini)

i = 1(香蕉)时,j将是2(椰子),然后是3(西葫芦)。

等等......

基本上,您确保元素j始终位于元素i 之前。这意味着您已经有效地删除了一半的可能性(其中j在 i之前是),这就是您想要的。

注意:如果你想使用相同元素集(Apple + Apple),第二个for循环需要改为:

    for(int j = i; j < myArray.Length; j++) //notice j = i instead of i + 1

答案 2 :(得分:0)

要返回所有排列(即(1,2)(2,1)),您可以使用SelectMany与自身交叉加入集合:

 var hashSet = new HashSet<int>{1,2,3,4,5,6,7,8};
 foreach (var elt in hashSet.SelectMany(
                     x => hashSet.Select(y => new Tuple<int, int>(x, y))))
 {
    Debug.WriteLine("{0}-{1}", elt.Item1, elt.Item2);
 }

修改:如果您只想要唯一的组合(即(1,2)而不是(2,1)),那么只需在交叉连接期间添加一个过滤器值更大的值:

 var hashSet = new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
 foreach (var elt in hashSet.SelectMany(
                     x => hashSet.Where(y => y >= x)
                                 .Select(y => new Tuple<int, int>(x, y))))
 {
    Debug.WriteLine("{0}-{1}", elt.Item1, elt.Item2);
 }

答案 3 :(得分:0)

您可以直接在HashSet上使用索引。 试试这个:

int int1, int2;
HashSet<int> hs = new HashSet<int>();
hs.Add(1);
hs.Add(2);
hs.Add(3);
for (int i = 0; i < hs.Count-1; i++) {
    int1 = hs.ElementAt<int>(i);
    for (int j = i + 1; j < hs.Count; j++)
    {
        int2 = hs.ElementAt<int>(j);
    }
}