C# Jagged Array,确定是否有任何对匹配

C# Jagged Array, determine if any pair matches

在下面的代码中,我在一个对象中填充了一个充满键(属性)的交错数组:

  foreach (var item in Items.Where(x => x.NeedsSaved))
        {
            int[][] valuePairs = new int[item.Collection.Count()][];

            int i = 0;

            foreach (var collectionItem in item.Collection)
            {
                valuePairs[i] = new int[4] { collectionItem.Id1, collectionItem.Id2, collectionItem.Id3, collectionItem.Id4};
                i++;
            }
                 -- TODO
         }

        return false;

我正在尝试确定是否存在任何重复的密钥对。我确定 Linq 缺少一些简单的东西。每对 4 个值都必须是唯一的,所以如果我在数组中有两个等于 :

的集合

{1, 1, 1, 1} 和 {1, 1, 1, 1}

该方法应该 return 正确。

但如果值是 {1, 1, 1, 1} {2, 1, 1, 1}

该方法应该return false。

进一步说明,目的是在客户端中保存时,我将发布到 API。该对象具有 (a, b, c, d) 的复合主键,因此我试图在 API 被命中之前通过验证警告用户,从而导致重复键异常。

感谢任何帮助,如果这是重复的,我深表歉意,我已尽我所能搜索答案。

根据 Xaver 的建议,我意识到使用 Jagged 数组会使事情变得更加复杂。

我最终使用了 IEquatable 接口,最终的解决方案如下所示:

   private bool HasDuplicateKeyValues()
    {
        foreach (var item in Items.Where(x => x.NeedsSaved))
        {
            List<KeyValues> keyPairs = new List<KeyValues>();

            foreach (var collectionItem in item.Collection)
            {
                keyPairs.Add(new KeyValues(collectionItem.Id1, collectionItem.Id2, collectionItem.Id3, collectionItem.Id4));
            }

            foreach (var kp in keyPairs)
            {
                if (keyPairs.Where(x => x.Equals(kp)).Count() > 1)
                {
                    return true;
                }
                else
                {
                    continue;
                }
            }
        }
        return false;
    }
}

public class KeyValues : IEquatable<KeyValues>
{
    public int Id1 { get; set; }
    public int Id2 { get; set; }
    public int Id3 { get; set; }
    public int Id4 { get; set; }

    public KeyValues(int id1, int id2, int id3, int id4)
    {
        Id1 = id1;
        Id2 = id2;
        Id3 = id3;
        Id4 = id4;
    }

    public bool Equals(KeyValues other)
    {
        return this.Id1 == other.Id1 && this.Id2 == other.Id2 && this.Id3 == other.Id3 && this.Id4 == other.Id4;
    }
}

感谢您的帮助!

所以实际上你有一个数组 Items。每个 Item 都是一个整数数组。

您想知道 Items 的序列是否“相等”。

你对平等的定义不是标准定义。如果我们想要一个可重用的非标准相等概念,我们必须写一个 IEqualityComparer<int[][]>.

这类似于 IEqualityComparer<string> 的所有版本来比较两个字符串。有默认的字符串比较器,还有 OrdinalIgnoreCase、InvariantCulture 等。您甚至可以创建自己的字符串比较器,例如忽略空格的。

写完 IEqualityComparer<int[][]> 后,用法如下:

IEqualityComparer<int[][]> comparer = ...
int[][] x = ...
int[][] y = ...
bool equal = comparer.Equals(x, y);

相等比较器通常来自 class EqualityComparer<T>

int[] 的相等比较器

让我们先为 int[]:

写一个 EqualityComparer

int[] xint[] y 是相等的,它们的长度相同,并且对于每个 0<i<length x[i] == y[i].

public class IntArrayComparer : EqualityComparer<int[]>
{
    public static IEqualityComparer<int[]> ByValue {get;} = new ArrayByValueComparer();

    public override bool Equals (int[] x, int[] y)
    {
        // for efficiency reasons, start every EqualityComparer with the same three lines
        if (x == null) return y == null;   // true if both null;
                                           // false if x null and y not null
        if (y == null) return false;       // false, because x not null
        if (object.ReferenceEqual(x, y) return true;:

        // do the actual equality check:
        return x.Length == y.Length && x.SequenceEqual(y);

        // or use the alternative without LINQ
        if (x.Length != y.Length) return false;
        for (int i=0; i<x.Length; ++i)
        {
            if (x[i] != y[i]) return false;
        }
        return true;
    }

    public override int GetHashCode (int[] x)
    {
        throw new NotImplementedException();
    }
}

用法:

IEqualityComparer<int[]> comparer = IntArrayComparer.ByValue;
int[] x = ...
int[] y = ...
bool equal = comparer.Equals(x, y);

间奏曲 GetHashCode

只有在需要快速查找的结构(如字典或哈希集)中使用它时,才需要 GetHashCode。 GetHashCode 的目的是有一种非常快速的方法来检测两个项目是否不同。不要检测它们是否相等!

例如,如果您有客户,每个客户都有一个 CustomerId,并且您检测到 x.CustomerId != y.CustomerId,那么您可以确定客户 x 不等于客户 y。但是,如果它们具有相同的 Id,则无法确定所有值是否相等。

因此 GetHashCode 的唯一要求是:“如果根据您对等式 x 等于 y 的定义,那么 x.GetHashCode() 应该 return 与 y.GetHashCode( ).

如果x不等于y,那么我们不知道它们是相等的还是不同的。不同的对象可能 return 相同的 HashCode。 Int64.GetHashCode() 很容易看出这一点。由于 return 值是一个 Int32,因此必须有多个 Int64 值具有相同的哈希码。

对于客户地址:如果您看到 Post 代码不同,您就知道客户住在不同的地址。所以只检查了一系列Customer Addresses的Post代码后,你可能已经扔掉了99.9%的Customers,你只需要检查最后的0.1%

一个好的哈希码可以快速检测出大多数 class 实例的差异。对于哈希码,您不必检查数组的所有值,只需检查表明不同的值即可。

所以在这种情况下,对于 HashCode,我们只检查长度和第一个元素。

public override int GetHashCode (int[] x)
{
    if (x ==  null) return 658779956;

    return x.Length.GetHashCode() * 77852364 + x[0].GetHashCode();
}

如果你的大部分内部数组都具有相同的长度,那么使用Length 来计算HashCode 是没有用的。测试最有可能不同的属性。

建议:我通常不实现 GetHashCode。如果稍后将 Comparer 用于查找结构(如字典),则会出现异常。然后你就可以实现GetHashCode了。

回到你的问题

既然我们已经了解了如何为 int[] 创建相等比较器,那么为 int[][] 创建相等比较器就很容易了。

class JaggedIntArrayComparer : EqulityComparer<int[][]>
{
    public static IEqualityComparer<int[][]> ByValue {get;} = new JaggedIntArrayComparer();

    // use the previously created Equality Comparer
    private static IEqualityComparer<int[]> IntArrayComparer {get;} = IntArryComparer.ByValue;

    public override bool Equals (int[][] x, int[][] y)
    {
         // The same three lines as in the previous comparer:
         if (x == null) return y == null;
         if (y == null) return false;
         if (Object.ReferenceEquals(x, y)) return true;

         return x.Length == y.Length && x.SequenceEqual(y, IntArrayComparer);

         // alternative without LINQ, similar to the previous comparer
         if (x.Length != y.Length) return false;
         for (int i=0; i< x.Length; ++i)
         {
             if (!IntArrayComparer.Equals(x[i], yi[])) return false;
         }
         return true;
    }

    // TODO: invent a simple GetHashCode;
}