Как использовать linq явно в powershell или аналоге & ldquo; NOT & ldquo; в SQL

У вас может быть удаленный столбец в таблице, который все еще занимает место. Также проверьте, что настройки «текст в строке» одинаковы.

3
задан Vasiliy Pupkin 3 March 2019 в 14:53
поделиться

2 ответа

Чтобы (общий) метод .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 гражданин.

0
ответ дан mklement0 3 March 2019 в 14:53
поделиться

Дополнить ответ на основе 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.

    • Обратите внимание, что объекты украшены элементом NoteProperty .SideIndicator, однако, с помощью ETS (расширенной системы типов) PowerShell - см. Ниже.
  • Учитывая, что Compare-Object выводит входные объекты, которые являются уникальными для или коллекции , Where-Object SideIndicator -eq '<=' необходимо использовать для ограничения результатов теми разностными объектами, которые являются уникальными на входную коллекцию LHS (которая сигнализируется через значение свойства .SideIndicator, равное '<=' - стрелка указывает в сторону, для которой объект уникален).

В этом выпуске GitHub предлагается ряд улучшений командлета Compare-Object, которые могут помочь упростить и ускорить приведенное выше решение.
Тем не менее, предложение сделать LINQ первоклассным гражданином PowerShell имеет гораздо большие перспективы.

0
ответ дан mklement0 3 March 2019 в 14:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: