programing

PowerShell에서 해시 테이블 병합: 어떻게?

skycolor 2023. 9. 4. 19:41
반응형

PowerShell에서 해시 테이블 병합: 어떻게?

두 개의 해시 테이블을 병합하려고 합니다. 두 번째 해시 테이블에 동일한 키가 있으면 첫 번째 해시 테이블에서 키-값 쌍을 덮어씁니다.

이를 위해 두 번째 해시 테이블에 동일한 키가 존재하는 경우 첫 번째 해시 테이블에서 모든 키-값 쌍을 제거하는 이 함수를 작성했습니다.

PowerShell에 한 줄씩 입력하면 작동합니다.그러나 전체 기능을 실행하면 PowerShell에서 각 개체에 대해 누락된 매개 변수를 제공하도록 요청합니다.

function mergehashtables($htold, $htnew)
{
    $htold.getenumerator() | foreach-object
    {
        $key = $_.key
        if ($htnew.containskey($key))
        {
            $htold.remove($key)
        }
    }
    $htnew = $htold + $htnew
    return $htnew
}

출력:

PS C:\> mergehashtables $ht $ht2

cmdlet ForEach-Object at command pipeline position 1
Supply values for the following parameters:
Process[0]:

$ht 및 $ht2는 각각 두 개의 키-값 쌍을 포함하는 해시 테이블이며, 그 중 하나는 두 해시 테이블에 키 "이름"을 포함합니다.

내가 뭘 잘못하고 있는 거지?

병합-해시

키를 제거하는 대신 키를 덮어쓰는 방법을 고려할 수 있습니다.

$h1 = @{a = 9; b = 8; c = 7}
$h2 = @{b = 6; c = 5; d = 4}
$h3 = @{c = 3; d = 2; e = 1}


Function Merge-Hashtables {
    $Output = @{}
    ForEach ($Hashtable in ($Input + $Args)) {
        If ($Hashtable -is [Hashtable]) {
            ForEach ($Key in $Hashtable.Keys) {$Output.$Key = $Hashtable.$Key}
        }
    }
    $Output
}

몇 할 수 두 의 입력 되지 않습니다. "cmdlet" " " " " " 있 " " 두 " 블 " 력 " 로 " 이 " 개 " 입 " 의 " 니 " 습 " 의 " 테 " 지 " 되 " 다 " 않 " 한 " 제 " 이 " 경 " 며 " 우 " 으 " 할 " 여 " 러 " 을 " 용 " 수 " 문 " 구 ": 파프라사용:$h1, $h2, $h3 | Merge-Hashtables
사용: 인수 사:Merge-Hashtables $h1 $h2 $h3
또는조합::$h1 | Merge-Hashtables $h2 $h3
위의 모든 예제는 동일한 해시 테이블을 반환합니다.

Name                           Value
----                           -----
e                              1
d                              2
b                              6
c                              3
a                              9

제공된 해시 테이블에 중복된 키가 있으면 마지막 해시 테이블의 값이 사용됩니다.


(2017-07-09 추가)

병합-해시 테이블 버전 2

일반적으로 "두 번째 키에 동일한 키가 존재하는 경우 첫 번째 키에서 키-값 쌍을 덮어씁니다"라는 원래 질문과 같이 특정 요구에 맞게 매개 변수를 사용자 지정할 수 있는 더 많은 글로벌 기능을 선호합니다.왜 첫 번째 사람이 아니라 마지막 사람이 지배하도록 내버려 두나요?왜 아무것도 제거해야 하나요?다른 사람이 값을 병합하거나 결합하거나 가장 큰 값을 얻거나 평균을 얻으려고 할 수 있습니다.
은 더 하는 것을 수된 값 하여 중복 에서 값 수 매개 를 가지고 .만 함수에 파이프할 수 있음), 현재 개체에 표시된 해시 키에 할당된 값 배열을 작동하여 중복 항목에서 값 배열을 처리하는 방법을 결정할 수 있는 매개 변수를 가지고 있습니다.$_).

기능.

Function Merge-Hashtables([ScriptBlock]$Operator) {
    $Output = @{}
    ForEach ($Hashtable in $Input) {
        If ($Hashtable -is [Hashtable]) {
            ForEach ($Key in $Hashtable.Keys) {$Output.$Key = If ($Output.ContainsKey($Key)) {@($Output.$Key) + $Hashtable.$Key} Else  {$Hashtable.$Key}}
        }
    }
    If ($Operator) {ForEach ($Key in @($Output.Keys)) {$_ = @($Output.$Key); $Output.$Key = Invoke-Command $Operator}}
    $Output
}

구문

HashTable[] <Hashtables> | Merge-Hashtables [-Operator <ScriptBlock>]

기본값 기본적으로 중복된 해시 테이블 항목의 모든 값이 배열에 추가됩니다.

PS C:\> $h1, $h2, $h3 | Merge-Hashtables

Name                           Value
----                           -----
e                              1
d                              {4, 2}
b                              {8, 6}
c                              {7, 5, 3}
a                              9

예: 버전 1(마지막 값 사용)과 동일한 결과를 얻으려면 다음 명령을 사용합니다.$h1, $h2, $h3 | Merge-Hashtables {$_[-1]}첫 번째 값을 대신 사용하려면 다음 명령을 사용합니다.$h1, $h2, $h3 | Merge-Hashtables {$_[0]}또는 가장 큰 값:$h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Maximum).Maximum}.

기타 예:

PS C:\> $h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Average).Average} # Take the average values"

Name                           Value
----                           -----
e                              1
d                              3
b                              7
c                              5
a                              9


PS C:\> $h1, $h2, $h3 | Merge-Hashtables {$_ -Join ""} # Join the values together

Name                           Value
----                           -----
e                              1
d                              42
b                              86
c                              753
a                              9


PS C:\> $h1, $h2, $h3 | Merge-Hashtables {$_ | Sort-Object} # Sort the values list

Name                           Value
----                           -----
e                              1
d                              {2, 4}
b                              {6, 8}
c                              {3, 5, 7}
a                              9

두 가지 문제가 있습니다.

  1. 는 개형브스다는같라은있합인니어다야에음과와 같은 줄에 .Foreach-object
  2. 컬렉션을 통해 열거하는 동안 컬렉션을 수정하면 안 됩니다.

아래 예제에서는 두 가지 문제를 모두 해결하는 방법을 보여 줍니다.

function mergehashtables($htold, $htnew)
{
    $keys = $htold.getenumerator() | foreach-object {$_.key}
    $keys | foreach-object {
        $key = $_
        if ($htnew.containskey($key))
        {
            $htold.remove($key)
        }
    }
    $htnew = $htold + $htnew
    return $htnew
}

새로운 답변이 아닙니다. 이것은 기능적으로 개선된 @Josh-Pett와 동일합니다.

다음 답변:

  • Merge-HashTable에서는 이 에 삽입하려면 올바른 합니다.
  • 실력이 부족하지 않았습니다.해시 테이블 입력의 복제를 추가했습니다. 그렇지 않으면 의도가 아닌 입력이 손상되었습니다.
  • 적절한 사용 예를 추가했습니다.
function Merge-HashTable {
    param(
        [hashtable] $default, # Your original set
        [hashtable] $uppend # The set you want to update/append to the original set
    )

    # Clone for idempotence
    $default1 = $default.Clone();

    # We need to remove any key-value pairs in $default1 that we will
    # be replacing with key-value pairs from $uppend
    foreach ($key in $uppend.Keys) {
        if ($default1.ContainsKey($key)) {
            $default1.Remove($key);
        }
    }

    # Union both sets
    return $default1 + $uppend;
}

# Real-life example of dealing with IIS AppPool parameters
$defaults = @{
    enable32BitAppOnWin64 = $false;
    runtime = "v4.0";
    pipeline = 1;
    idleTimeout = "1.00:00:00";
} ;
$options1 = @{ pipeline = 0; };
$options2 = @{ enable32BitAppOnWin64 = $true; pipeline = 0; };

$results1 = Merge-HashTable -default $defaults -uppend $options1;
# Name                           Value
# ----                           -----
# enable32BitAppOnWin64          False
# runtime                        v4.0
# idleTimeout                    1.00:00:00
# pipeline                       0

$results2 = Merge-HashTable -default $defaults -uppend $options2;
# Name                           Value
# ----                           -----
# idleTimeout                    1.00:00:00
# runtime                        v4.0
# enable32BitAppOnWin64          True
# pipeline                       0

전체 해시 테이블 트리를 병합하려는 경우

function Join-HashTableTree {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [hashtable]
        $SourceHashtable,

        [Parameter(Mandatory = $true, Position = 0)]
        [hashtable]
        $JoinedHashtable
    )

    $output = $SourceHashtable.Clone()

    foreach ($key in $JoinedHashtable.Keys) {
        $oldValue = $output[$key]
        $newValue = $JoinedHashtable[$key]

        $output[$key] =
        if ($oldValue -is [hashtable] -and $newValue -is [hashtable]) { $oldValue | ~+ $newValue }
        elseif ($oldValue -is [array] -and $newValue -is [array]) { $oldValue + $newValue }
        else { $newValue }
    }

    $output;
}

그런 다음 다음과 같이 사용할 수 있습니다.

Set-Alias -Name '~+' -Value Join-HashTableTree -Option AllScope

@{
    a = 1;
    b = @{
        ba = 2;
        bb = 3
    };
    c = @{
        val = 'value1';
        arr = @(
            'Foo'
        )
    }
} |

~+ @{
    b = @{
        bb = 33;
        bc = 'hello'
    };
    c = @{
        arr = @(
            'Bar'
        )
    };
    d = @(
        42
    )
} |

ConvertTo-Json

다음과 같은 출력이 생성됩니다.

{
  "a": 1,
  "d": 42,
  "c": {
    "val": "value1",
    "arr": [
      "Foo",
      "Bar"
    ]
  },
  "b": {
    "bb": 33,
    "ba": 2,
    "bc": "hello"
  }
}

저는 이것이 필요했고 다음과 같은 효과가 있다는 것을 발견했습니다.

$HT += $HT2

『 』의 $HT2의 됩니다.$HT.

는 열린브스다같음라은있합니어다야인에과는레와 같은 .ForEach-Object또는 줄 연속 문자(백티크)를 사용해야 합니다.

{} 내의 코드가 이에 해당하기 때문입니다.정말로 가치가 있습니다.-Process의 매 변 수 개의 파라미터.ForEach-Objectsmdlet.

-Process <ScriptBlock[]> 
Specifies the script block that is applied to each incoming object.

이렇게 하면 당면한 문제를 해결할 수 있습니다.

(기존 키를 덮어쓰지 않고) 병합할 수 있는 가장 컴팩트한 코드는 다음과 같습니다.

function Merge-Hashtables($htold, $htnew)
{
   $htnew.keys | where {$_ -notin $htold.keys} | foreach {$htold[$_] = $htnew[$_]}
}

PowerShellUnion and Intersection of Hashtables에서 빌렸습니다.

저는 해시 테이블의 기본 속성이 해시 테이블의 항목에 의해 오버라이드(또는 오버로드)되었을 수 있으므로 일반 함수에서 해시 테이블의 기본 속성을 무차별적으로 참조해서는 안 된다는 점을 지적하고 싶었습니다.

예를 들어 해시 테이블$hash=@{'keys'='lots of them'}인 기본해테속성갖을됩게니다이블시▁the를 갖게 .Keyskeys 래서하것는그▁a것▁and▁thus는▁doing.foreach ($key in $hash.Keys) 항목 대 해 항 된 을 열 합 니거다를 열거합니다.keys의 값은 속성 Keys.

"GetEnumerator" 메서드를 사용합니다.keysPSBase재정의할 수 없는 속성은 기본 속성이 재정의되었는지 여부를 모를 수 있는 함수에서 사용해야 합니다.

따라서 Jon Z의 답변이 가장 좋습니다.

테이블에서 키을 '하려면상속')$htOld hashtables하위테이블시($htNew 테이블에 하지 않고, ), 를 참조하십시오.

function MergeHashtable($htOld, $htNew)
{
    $htOld.Keys | %{
        if (!$htNew.ContainsKey($_)) {
            $htNew[$_] = $htOld[$_];
        }
    }
    return $htNew;
}

이것은 다음을 수정할 것입니다.$htNew물건.

다음은 파이프라인을 사용하지 않는 함수 버전입니다(파이프라인이 나쁘다는 것이 아니라 다른 방법일 뿐입니다).또한 병합된 해시 테이블을 반환하고 원본을 변경하지 않은 상태로 둡니다.

function MergeHashtable($a, $b)
{
    foreach ($k in $b.keys)
    {
        if ($a.containskey($k))
        {
            $a.remove($k)
        }
    }

    return $a + $b
}

저는 jon Z의 답변을 확장하거나 단순화하고 싶었습니다.행이 너무 많고 Where-Object를 사용할 수 없는 것 같습니다.단순화된 버전은 다음과 같습니다.

Function merge_hashtables($htold, $htnew) {
    $htold.Keys | ? { $htnew.ContainsKey($_) } | % {
        $htold.Remove($_)
    }
    $htold += $htnew
    return $htold
}

언급URL : https://stackoverflow.com/questions/8800375/merging-hashtables-in-powershell-how

반응형