C# 比较两个数组并对匹配项执行操作
C# Compare two Arrays and perform Action on Matches
我的问题是:如何比较两个数组,并对两个数组中的元素执行操作?
我使用 C#/LINQ
我正在尝试做的事情:遍历一组用户。另一个数组,包含 some / specific 用户的规则。因此,对于规则数组中有规则的每个用户,在用户对象上增加一个字段。
我已经尝试过使用 Linq:
var array1 = context.SomeSecret.ToArray();
var array2 = anotherContext.AnotherSecret.ToArray();
(from rule in array2
from user in array1
where user.ID = rule.ID
select user).ToObserveable().Subscribe<User>(x => x.MaxRules++);
我正在尝试做的事情:遍历一组用户。另一个数组,包含 some / specific 用户的规则。因此,对于规则数组中有规则的每个用户,更新用户对象上的字段。
这是原始代码:
var userDic = context.SomeSecret.ToDictionary(u => u.ID);
var rules = anotherContext.AnotherSecret.ToList();
foreach(var rule in rules)
{
if(userDic.ContainsKey(rule.UserID))
{
userDic[rule.UserID]++;
}
}
user.ID
和 rule.UserID
相同。
注:
这是“毫无意义”的代码
有什么“优雅”的方法可以解决这个问题吗?
提前致谢。
您试图在几句话中做太多事情。这使您的代码难以阅读、难以重用、难以更改和难以进行单元测试。考虑养成制作可重复使用的小方法的习惯。
IEnumerable<Secret> GetSecrets() {...}
IEnumerable<Secret> GetOtherSecrets() {...}
How can i compare two arrays, and perform an action on the Elements that are in both?
LINQ 只能从您的源数据中提取数据。 LINQ 无法更改源数据。要更改源数据,您应该枚举使用 LINQ 提取的数据。这通常使用 foreach
.
来完成
所以你有两个 Secrets
序列,你想提取两个序列中的所有 Secrets
。
定义平等
首先,您需要指定:什么时候在两个序列中都是Secret:
Secret a = new Secret();
Secret b = a;
Secret c = (Secret)a.Clone();
很明显a和b指的是同一个对象。 Secret a和Secret c虽然所有属性和字段的值都相等,但它们是不同的实例。
效果是,如果您更改 Secret a 的其中一个属性的值,那么 Secret b 中的值也会更改。但是,Secret C保持不变。
Secret d = new Secret();
Secret e = new Secret();
IEnumerable<Secret> array1 = new Secret[] {a, d};
IEnumerable<Secret> array2 = new Secret[] {a, b, c, e};
很明显,您希望最终结果为 a
。您还需要 b
,因为 a 和 b 指的是同一个对象。同样清楚的是,您不想在最终结果中使用 d 和 e。但在您看来 a
和 c
相等吗?
您的要求中的另一个歧义:
IEnumerable<Secret> array1 = new Secret[] {a};
IEnumerable<Secret> array2 = new Secret[] {a, a, a, a, a};
你想要多少次最终结果?
相等比较器
默认情况下 a 和 c 是不同的对象,a == c
产生 false
.
但是如果你想定义它们相等,你需要在你的 LINQ 中说:不要使用相等的标准定义,使用我的相等定义。
为此我们需要编写一个相等比较器。或者更准确地说:创建一个实现 IEqualityComparer<Secret>
.
的 class 对象
幸运的是,这通常很简单。
Definition: Two objects of type Secret are equal if all properties return the same value.
class SecretComparer : EqualityComparer<Secret>
{
public static IEqualityComparer<Secret> ByValue {get;} = new SecretComparer();
public override bool Equals (Secret x, Secret y)
{
... // TODO: implement
}
public override int GetHashCode (Secret x)
{
... // TODO: implement
}
执行如下
我从 class EqualityComparer<Secret>
派生而不只是实现 IEqualityComparer<Secret>
的原因是 class EqualityComparer 也给我 属性 Default
,如果您想在比较两个 Secret 时使用默认定义,这可能会有用。
LINQ:获取两个序列中的对象
一旦有了相等比较器,LINQ 就会很简单。
为了提取 x 和 y 中的秘密,我使用了 Enumerable.Intersect 的重载,它使用了一个相等比较器:
IEnumerable<Secret> ExtractDuplicateSecrets(IEnumerable<Secret> x, IEnumerable<Secret> y)
{
return x.Intersect(y, SecretComparer.ByValue);
}
就是这样。要对每个剩余的 Secret 执行操作,请使用 foreach:
void PerformSecretAction(IEnumerable<Secret> secrets)
{
foreach (Secret secret in secrets)
{
secret.Process();
}
}
所以你的完整代码:
IEnumerable<Secret> x = GetSecrets();
IEnumerable<Secret> y = GetOtherSecrets();
IEnumerable<Secret> secretsInXandY = ExtractDuplicateSecrets(x, y);
PerformSecretAction(secretsInXandY);
或者如果您想在一条语句中执行此操作。不确定这是否提高了可读性:
PerformSecretAction(ExtractDuplicateSecrets(GetSecrets(), GetOtherSecrets());
制作小方法的好处:创建 x 和 y,一个 SecretComparer,提取公共 Secrets 并对所有剩余的 Secrets 执行操作,因为大多数过程都非常小,因此易于阅读。此外,所有过程都可以重复用于其他目的。您可以轻松更改它们(不同的相等定义:只需编写不同的比较器!),并且易于单元测试。
实施秘密平等
public override bool Equals (Secret x, Secret y)
{
// almost all equality comparers start with the following lines:
if (x == null) return y == null; // True if x and y both null
if (y == null) return false; // because x not null
if (Object.ReferenceEquals(x, y) return true; // same object
大多数时候我们不希望不同的派生 class 是相等的:因此 TopSecret
(派生自 Secret)不等于 Secret
。
if (x.GetType() != y.GetType()) return false;
剩下的取决于您对两个 Secret 何时相等的定义。大多数情况下,您会检查所有属性。有时你只检查一个小节。
return x.Id == y.Id
&& x.Description == y.Description
&& x.Date == y.Date
&& ...
在这里你可以看到代码取决于你对平等的定义。也许描述检查不区分大小写:
private static IEqualityComparer<string> descriptionComparer {get;}
= StringComparer.CurrentCultureIgnoreCase;
return x.Id == y.Id
&& descriptionComparer.Equals(x.Description, y.Description)
&& ...
实现 GetHashCode
这个方法主要是为了有一个快速判断两个对象不相等的方法。一个好的 GetHashCode 速度很快,并且会丢弃大多数不相等的对象。
只有一个要求:如果x和y被认为是相等的,它们应该return相同的HashCode。不是相反:不同的对象可能有相同的 Hashcode,尽管如果它们有不同的 HashCode 会更好。
这个怎么样:
public override int GetHashCode (Secret x)
{
if (x == null)
return 8744523; // just a number;
else
return x.Id.GetHashCode(); // only check Id
}
在上面的代码中,我假设 Secret 的 Id 是相当唯一的。可能只有在更新 Secret 时,您才会发现两个具有相同 ID 的不相等 Secret:
Secret existingSecret = this.FindSecretById(42);
Secret secretToEdit = (Secret)existingSecret.Clone();
secretToEdit.Description = this.ReadNewDescription();
现在 existingSecret 和 secretToEdit 具有相同的 Id
值,但描述不同。因此它们不相等。然而他们有相同的哈希码。
不过,到目前为止,大多数 Secret 都会有一个唯一的 ID,GetHashCode 将是一种非常快速的方法来检测两个 Secret 是否不同。
我的问题是:如何比较两个数组,并对两个数组中的元素执行操作? 我使用 C#/LINQ
我正在尝试做的事情:遍历一组用户。另一个数组,包含 some / specific 用户的规则。因此,对于规则数组中有规则的每个用户,在用户对象上增加一个字段。
我已经尝试过使用 Linq:
var array1 = context.SomeSecret.ToArray();
var array2 = anotherContext.AnotherSecret.ToArray();
(from rule in array2
from user in array1
where user.ID = rule.ID
select user).ToObserveable().Subscribe<User>(x => x.MaxRules++);
我正在尝试做的事情:遍历一组用户。另一个数组,包含 some / specific 用户的规则。因此,对于规则数组中有规则的每个用户,更新用户对象上的字段。
这是原始代码:
var userDic = context.SomeSecret.ToDictionary(u => u.ID);
var rules = anotherContext.AnotherSecret.ToList();
foreach(var rule in rules)
{
if(userDic.ContainsKey(rule.UserID))
{
userDic[rule.UserID]++;
}
}
user.ID
和 rule.UserID
相同。
注:
这是“毫无意义”的代码
有什么“优雅”的方法可以解决这个问题吗?
提前致谢。
您试图在几句话中做太多事情。这使您的代码难以阅读、难以重用、难以更改和难以进行单元测试。考虑养成制作可重复使用的小方法的习惯。
IEnumerable<Secret> GetSecrets() {...}
IEnumerable<Secret> GetOtherSecrets() {...}
How can i compare two arrays, and perform an action on the Elements that are in both?
LINQ 只能从您的源数据中提取数据。 LINQ 无法更改源数据。要更改源数据,您应该枚举使用 LINQ 提取的数据。这通常使用 foreach
.
所以你有两个 Secrets
序列,你想提取两个序列中的所有 Secrets
。
定义平等
首先,您需要指定:什么时候在两个序列中都是Secret:
Secret a = new Secret();
Secret b = a;
Secret c = (Secret)a.Clone();
很明显a和b指的是同一个对象。 Secret a和Secret c虽然所有属性和字段的值都相等,但它们是不同的实例。
效果是,如果您更改 Secret a 的其中一个属性的值,那么 Secret b 中的值也会更改。但是,Secret C保持不变。
Secret d = new Secret();
Secret e = new Secret();
IEnumerable<Secret> array1 = new Secret[] {a, d};
IEnumerable<Secret> array2 = new Secret[] {a, b, c, e};
很明显,您希望最终结果为 a
。您还需要 b
,因为 a 和 b 指的是同一个对象。同样清楚的是,您不想在最终结果中使用 d 和 e。但在您看来 a
和 c
相等吗?
您的要求中的另一个歧义:
IEnumerable<Secret> array1 = new Secret[] {a};
IEnumerable<Secret> array2 = new Secret[] {a, a, a, a, a};
你想要多少次最终结果?
相等比较器
默认情况下 a 和 c 是不同的对象,a == c
产生 false
.
但是如果你想定义它们相等,你需要在你的 LINQ 中说:不要使用相等的标准定义,使用我的相等定义。
为此我们需要编写一个相等比较器。或者更准确地说:创建一个实现 IEqualityComparer<Secret>
.
幸运的是,这通常很简单。
Definition: Two objects of type Secret are equal if all properties return the same value.
class SecretComparer : EqualityComparer<Secret>
{
public static IEqualityComparer<Secret> ByValue {get;} = new SecretComparer();
public override bool Equals (Secret x, Secret y)
{
... // TODO: implement
}
public override int GetHashCode (Secret x)
{
... // TODO: implement
}
执行如下
我从 class EqualityComparer<Secret>
派生而不只是实现 IEqualityComparer<Secret>
的原因是 class EqualityComparer 也给我 属性 Default
,如果您想在比较两个 Secret 时使用默认定义,这可能会有用。
LINQ:获取两个序列中的对象
一旦有了相等比较器,LINQ 就会很简单。 为了提取 x 和 y 中的秘密,我使用了 Enumerable.Intersect 的重载,它使用了一个相等比较器:
IEnumerable<Secret> ExtractDuplicateSecrets(IEnumerable<Secret> x, IEnumerable<Secret> y)
{
return x.Intersect(y, SecretComparer.ByValue);
}
就是这样。要对每个剩余的 Secret 执行操作,请使用 foreach:
void PerformSecretAction(IEnumerable<Secret> secrets)
{
foreach (Secret secret in secrets)
{
secret.Process();
}
}
所以你的完整代码:
IEnumerable<Secret> x = GetSecrets();
IEnumerable<Secret> y = GetOtherSecrets();
IEnumerable<Secret> secretsInXandY = ExtractDuplicateSecrets(x, y);
PerformSecretAction(secretsInXandY);
或者如果您想在一条语句中执行此操作。不确定这是否提高了可读性:
PerformSecretAction(ExtractDuplicateSecrets(GetSecrets(), GetOtherSecrets());
制作小方法的好处:创建 x 和 y,一个 SecretComparer,提取公共 Secrets 并对所有剩余的 Secrets 执行操作,因为大多数过程都非常小,因此易于阅读。此外,所有过程都可以重复用于其他目的。您可以轻松更改它们(不同的相等定义:只需编写不同的比较器!),并且易于单元测试。
实施秘密平等
public override bool Equals (Secret x, Secret y)
{
// almost all equality comparers start with the following lines:
if (x == null) return y == null; // True if x and y both null
if (y == null) return false; // because x not null
if (Object.ReferenceEquals(x, y) return true; // same object
大多数时候我们不希望不同的派生 class 是相等的:因此 TopSecret
(派生自 Secret)不等于 Secret
。
if (x.GetType() != y.GetType()) return false;
剩下的取决于您对两个 Secret 何时相等的定义。大多数情况下,您会检查所有属性。有时你只检查一个小节。
return x.Id == y.Id
&& x.Description == y.Description
&& x.Date == y.Date
&& ...
在这里你可以看到代码取决于你对平等的定义。也许描述检查不区分大小写:
private static IEqualityComparer<string> descriptionComparer {get;}
= StringComparer.CurrentCultureIgnoreCase;
return x.Id == y.Id
&& descriptionComparer.Equals(x.Description, y.Description)
&& ...
实现 GetHashCode
这个方法主要是为了有一个快速判断两个对象不相等的方法。一个好的 GetHashCode 速度很快,并且会丢弃大多数不相等的对象。
只有一个要求:如果x和y被认为是相等的,它们应该return相同的HashCode。不是相反:不同的对象可能有相同的 Hashcode,尽管如果它们有不同的 HashCode 会更好。
这个怎么样:
public override int GetHashCode (Secret x)
{
if (x == null)
return 8744523; // just a number;
else
return x.Id.GetHashCode(); // only check Id
}
在上面的代码中,我假设 Secret 的 Id 是相当唯一的。可能只有在更新 Secret 时,您才会发现两个具有相同 ID 的不相等 Secret:
Secret existingSecret = this.FindSecretById(42);
Secret secretToEdit = (Secret)existingSecret.Clone();
secretToEdit.Description = this.ReadNewDescription();
现在 existingSecret 和 secretToEdit 具有相同的 Id
值,但描述不同。因此它们不相等。然而他们有相同的哈希码。
不过,到目前为止,大多数 Secret 都会有一个唯一的 ID,GetHashCode 将是一种非常快速的方法来检测两个 Secret 是否不同。