不区分大小写 [Linq.Enumerable]::SequenceEqual()

Case insensitive [Linq.Enumerable]::SequenceEqual()

我一直在使用[Linq.Enumerable]::SequenceEqual()来比较两个数组中项目的顺序,例如

$validOrder = @('one', 'two', 'three')
$provided = @('one', 'two', 'three')
[Linq.Enumerable]::SequenceEqual($validOrder, $provided)

这行得通,但现在我意识到我想独立解决大写错误,所以我想以不区分大小写的方式测试顺序。 我发现 this that documents a different method signature, with IEqualityComparer<T> as the third value. That sure seems like the right direction, but I haven't found anything that helps me implement this in powershell. I tried just using 'OrdinalIgnoreCase' for the last argument, which works for [String].FindIndex() as was pointed out in another thread. But alas not here. I also found 实际上为不同的对象类型创建了一个自定义比较器,但这似乎我只是在手动实现我真正想要的东西,而且我不确定使用 [= 的价值是什么13=],我可以将我的数组传递给我的 class 方法并直接在那里完成工作。

我也让这个方法奏效了 如果(-不(比较对象-SyncWindow 0 $validOrder $provided)){ $结果 = 'ordered' } 别的 { $结果 = 'disordered' } 而且它已经不区分大小写了。但它也比较慢,而且我可能有很多这样的测试要做,所以速度会有所帮助。

最后我发现这似乎有效,而且超级简单,不区分大小写(如果我愿意的话)并且看起来很快。数组中的项目总数总是很小,不同数组的重复次数是性能问题。

$result = ($provided -join ' ') -eq ($validOrder -join ' ')

那么,最后一个选项是否可行,或者我是否遗漏了一些明显的反对意见?

另外,我觉得在其他情况下 IEqualityComparer<T> 是一个有用的论点,所以知道如何去做会很有用。假设我没看错,IEqualityComparer<T> 提供了一种不同形式的比较机制,而不仅仅是滚动我自己的比较。

I tried just using 'OrdinalIgnoreCase' for the last argument, ...

很接近:

$lower = 'a b c'.Split()
$upper = 'A B C'.Split()
$ignoreCaseComparer = [System.StringComparer]::OrdinalIgnoreCase

[Linq.Enumerable]::SequenceEqual($lower, $upper, $ignoreCaseComparer)

StringComparer class - NOT to be confused with the StringComparison 枚举类型 - 实现 IEqualityComparer<string>,因此满足类型约束。

请注意,上述方法调用仅适用于我使用 String.Split() 创建方法参数,并且 Split() 显式 returns a [string[]] - 对于大多数其他类似数组的表达式,您可能会发现自己需要显式键入输入参数,以便 PowerShell 传递正确的类型参数:

# This will fail - type of `@()` is [object[]], 
# and `SequenceEquals<object>()` doesn't accept stringcomparer
$lower = @(-split 'a b c')
$upper = @(-split 'A B C')
$ignoreCaseComparer = [System.StringComparer]::OrdinalIgnoreCase

[Linq.Enumerable]::SequenceEqual($lower, $upper, $ignoreCaseComparer)
# This will succeed thanks to the cast(s) to [string[]] at the call site
$lower = @(-split 'a b c')
$upper = @(-split 'A B C')
$ignoreCaseComparer = [System.StringComparer]::OrdinalIgnoreCase

[Linq.Enumerable]::SequenceEqual([string[]]$lower, [string[]]$upper, $ignoreCaseComparer)

可能不是您确切问题的最佳答案,但留在这里供后代使用...

如果您想使用自己的自定义比较器(例如,在比较之前反转一个数组中的值),您可以这样做:

$expected = @("one", "two", "three");
$actual   = @("eno", "owt", "eerht");

# see 
class ReversingComparer : System.Collections.Generic.IEqualityComparer[string] {
  [bool]Equals([string]$x, [string]$y) { return $x -eq ($y[-1..-$y.Length] -join ''); }
  [int]GetHashCode([string] $x) { return $x.GetHashCode(); }
}

[ReversingComparer] $comparer = [ReversingComparer]::new();

[Linq.Enumerable]::SequenceEqual([string[]]$expected, [string[]]$actual, $comparer);
# true