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[] x
和 int[] 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;
}
在下面的代码中,我在一个对象中填充了一个充满键(属性)的交错数组:
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[]
:
int[] x
和 int[] 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;
}