C# 比较两个数组并对匹配项执行操作



我的问题是:如何比较两个数组,并对两个数组中的元素执行操作? 我使用 C#/LINQ

我想做的是:循环遍历一系列用户。另一个数组,包含某些/特定用户的规则。因此,对于在 rules 数组中具有规则的每个用户,在用户对象上增加一个字段。

我已经尝试过使用 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++);

我想做的是:循环遍历一系列用户。另一个数组,包含某些/特定用户的规则。因此,对于在 rules 数组中具有规则的每个用户,更新用户对象上的字段。

这是原始代码:

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.IDrule.UserID是一样的。

注意:
这是"无意义"的代码

有没有">优雅"的方法可以解决这个问题?

提前谢谢。

你试图在一些陈述中做太多。这使得你的代码难以阅读、难以重用、难以更改和难以单元测试。考虑养成制作小型可重用方法的习惯。

IEnumerable<Secret> GetSecrets() {...}
IEnumerable<Secret> GetOtherSecrets() {...}

如何比较两个数组,并对两个数组中的元素执行操作?

LINQ 只能从源数据中提取数据。LINQ 无法更改源数据。若要更改源数据,应枚举使用 LINQ 提取的数据。这通常使用foreach.

所以你有两个Secrets序列,并且你想提取两个序列中的所有Secrets

定义平等

首先,您需要指定:两个序列中的密钥何时为:

Secret a = new Secret();
Secret b = a;
Secret c = (Secret)a.Clone();

很明显,a和b指的是同一个对象。尽管机密 a 和机密 c 中的所有属性和字段的值相等,但它们是不同的实例。

其效果是,如果您更改机密 a 的一个属性的值,则该值也会在机密 b 中更改。但是,秘密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。但在您看来ac是平等的吗?

您的要求中的另一个歧义:

IEnumerable<Secret> array1 = new Secret[] {a};
IEnumerable<Secret> array2 = new Secret[] {a, a, a, a, a};

您希望在最终结果中出现多少次?

相等比较器

默认情况下,a 和 c 是不同的对象,a == c产生false

但是,如果要将它们定义为相等,则需要在 LINQ 中说明:不要使用相等的标准定义,使用我对相等的定义。

为此,我们需要编写一个相等比较器。或者更准确地说:创建一个实现IEqualityComparer<Secret>的类的对象。

幸运的是,这通常非常简单。

定义:如果所有属性返回相同的值,则 Secret 类型的两个对象相等。

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
}

实施如下

我从类EqualityComparer<Secret>派生的原因是,而不仅仅是实现IEqualityComparer<Secret>,是因为类EqualComparer也给了我属性Default,如果你想在比较两个秘密时使用默认定义,这可能很有用。

LINQ:获取两个序列中的对象

有了相等比较器后,LINQ 将非常简单。 为了提取同时位于 x 和 y 中的机密,我使用了使用相等比较器的 Enumerable.Intersect 重载:

IEnumerable<Secret> ExtractDuplicateSecrets(IEnumerable<Secret> x, IEnumerable<Secret> y)
{
return  x.Intersect(y, SecretComparer.ByValue);
}

就这样。要对剩余的每个密钥执行操作,请使用 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,提取公共秘密并对所有剩余的秘密执行操作,是大多数过程将非常小,因此易于阅读。此外,所有过程都可以重用于其他目的。你可以很容易地改变它们(相等的不同定义:只需写一个不同的比较器!),并且很容易进行单元测试。

实现机密相等

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

大多数时候,我们通常不希望不同的派生类相等:因此,TopSecret(派生自 Secret)不等于Secret

if (x.GetType() != y.GetType()) return false;

其余的取决于您对两个机密何时相等的定义。大多数情况下,您会检查所有属性。有时您只检查一个小节。

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 被认为是相等的,它们应该返回相同的哈希码。反之则不然:不同的对象可能具有相同的哈希码,尽管如果它们具有不同的哈希码会更好。

这个怎么样:

public override int GetHashCode (Secret x)
{
if (x == null)
return 8744523; // just a number;
else
return x.Id.GetHashCode();  // only check Id
}

在上面的代码中,我假设密钥的 Id 是相当唯一的。可能只有在更新密钥时,您才会发现两个具有相同 ID 的非相等密钥:

Secret existingSecret = this.FindSecretById(42);
Secret secretToEdit = (Secret)existingSecret.Clone();
secretToEdit.Description = this.ReadNewDescription();

现在 existingSecret 和 secretToEdit 对Id具有相同的值,但描述不同。因此,它们是不平等的。然而,它们具有相同的哈希码。

尽管如此,到目前为止,大多数秘密都有一个唯一的 ID,GetHashCode 将是一种非常快速的方法来检测两个秘密的不同。

相关内容

  • 没有找到相关文章

最新更新