У вас может быть удаленный столбец в таблице, который все еще занимает место. Также проверьте, что настройки «текст в строке» одинаковы.
Чтобы (общий) метод .Except()
LINQ работал, два перечислимых значения ( IEnumerable<T>
), передаваемых в качестве аргументов, должны:
T
IEquatable<T>
. PowerShell, по-видимому, не может найти правильную перегрузку для .Except()
с массивами [object[]]
, возвращаемыми $Table1.FindName1
и $Table2.FindName1
, хотя эти массивы технически соответствуют вышеуказанным требованиям - я не знаю почему.
Однако, просто приведение этих массивов к тому, что уже есть - [object[]]
- решает проблему:
[Linq.Enumerable]::Except([object[]] $Table1.FindName1, [object[]] $Table2.FindName1)
Учитывая, что столбец .FindName1
в конечном итоге содержит строки , вы также может приводить к [string[]]
, хотя в моих неформальных тестах это снова не показывало производительность, по крайней мере с вашими примерами данных.
Теперь, если вы хотите вернуть целых строк , используя столбец .FindName1
только для сравнения , все становится намного сложнее: [ 1134]
Вы должны реализовать пользовательский класс сравнения, который реализует интерфейс IEqualityComparer[T]
.
Необходимо преобразовать коллекцию .Rows
таблиц данных в IEnumerable[DataRow]
, что требует вызова метода System.Linq.Enumerable.Cast () через отражение .
[DataRow[]]
, это может привести к неэффективному преобразованию коллекции строк в массив. Вот решение PSv5 +, которое реализует пользовательский класс сравнения в качестве класса PowerShell:
# A custom comparer class that compares two DataRow instances by their
# .FindName1 column.
class CustomTableComparer : Collections.Generic.IEqualityComparer[Data.DataRow] {
[bool] Equals([Data.DataRow] $x, [Data.DataRow] $y) {
return [string]::Equals($x.FindName1, $y.FindName1, 'Ordinal')
}
[int] GetHashCode([Data.DataRow] $row) {
# Note: Any two rows for which Equals() returns $true must return the same
# hash code. Because *ordinal, case-sensitive* string comparison is
# used above, it's sufficient to simply call .GetHashCode() on
# the .FindName1 property value, but that would have to be tweaked
# for other types of string comparisons.
return $row.FindName1.GetHashCode();
}
}
# Use reflection to get a reference to a .Cast() method instantiation
# that casts to IEnumerable<DataRow>.
$toIEnumerable = [Linq.Enumerable].GetMethod('Cast').MakeGenericMethod([Data.DataRow])
# Call .Except() with the casts and the custom comparer.
# Note the need to wrap the .Rows value in an aux. single-element
# array - (, ...) - for it to be treated as a single argument.
[Linq.Enumerable]::Except(
$toIEnumerable.Invoke($null, (, $Table1.Rows)),
$toIEnumerable.Invoke($null, (, $Table2.Rows)),
[CustomTableComparer]::new()
)
В этом выпуске GitHub предлагается сделать LINQ первым Класс PowerShell гражданин.
Дополнить ответ на основе LINQ собственным решением PowerShell :
Командлет Compare-Object
позволяет сравнивать коллекции , но обратите внимание, что хотя он более краткий , он также намного медленнее, чем решение на основе LINQ :
Compare-Object -PassThru -Property FindName1 `
([Data.DataRow[]] $Table1.Rows) `
([Data.DataRow[]] $Table2.Rows) | Where-Object SideIndicator -eq '<='
Casting [Data.DataRow[]]
- который создает новый массив из коллекции строк - по-видимому, необходим для Compare-Object
распознавания строк как перечислимых.
.GetEnumerator()
или приведение к Collections.IEnumerable
не помогают, а приведение к Collections.Generic.IEnumerable[Data.DataRow]]
не удается. -Property FindName1
задает свойство сравнения, то есть свойство для сравнения строк по.
-PassThru
необходимо, чтобы Compare-Object
выводил входные объекты как есть, вместо пользовательских объектов, которые содержат только свойства, указанные в -Property
.
.SideIndicator
, однако, с помощью ETS (расширенной системы типов) PowerShell - см. Ниже. Учитывая, что Compare-Object
выводит входные объекты, которые являются уникальными для или коллекции , Where-Object SideIndicator -eq '<='
необходимо использовать для ограничения результатов теми разностными объектами, которые являются уникальными на входную коллекцию LHS (которая сигнализируется через значение свойства .SideIndicator
, равное '<='
- стрелка указывает в сторону, для которой объект уникален).
В этом выпуске GitHub предлагается ряд улучшений командлета Compare-Object
, которые могут помочь упростить и ускорить приведенное выше решение.
Тем не менее, предложение сделать LINQ первоклассным гражданином PowerShell имеет гораздо большие перспективы.