2017-09-27 6 views
0

Ich versuche den besten Weg zu finden, die in einem Array gespeicherten Hashtabellen zurückzugeben, die alle Eigenschaften einer anderen Hashtabelle entsprechen, die separat ist aus dem Array.Wie man Eigenschaften einer Hashtabelle mit einem Array von Hashtabellen in Powershell vergleicht

Zum Beispiel, ich habe die folgende Array und Hash Table Variablen:

$myarr = @(
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"}, 
    @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"}, 
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"}) 

$crit = @{"first"="A";"third"="C"} 

Ich brauche eine Art und Weise jede gesamte Hash Table in dem Array gespeichert zurückzukehren, wo alle Eigenschaften von $crit abgestimmt sind. In diesem Beispiel möchte ich $myarr[0] und $myarr[2] zurückgegeben werden.

Ich kann dies erreichen, indem ich nacheinander die Eigenschaften von $crit durchlaufen und sie mit jeder Hash-Tabelle im Array vergleichen, aber ich wollte sehen, ob es einen besseren Weg gibt, die Hash-Tabellen zu vergleichen kann nicht herausfinden, ähnlich wie Compare-Object mit Arrays.

ForEach ($hash in $myarr) { 
    $match = $true 
    ForEach ($key in $crit.Keys) {If ($hash.$key -ne $crit.$key) {$match = $false;Break}} 
    If ($match) {$hash}} 

Das Endziel hierfür ist der Vergleich mit der geringsten Menge an Speichernutzung zu tun, da die reale Anwendung sein wird Hunderttausende dieser Arrays zu vergleichen, mehrere hundert Hash Tables enthält, die alle 100 haben + Eigenschaften. Natürlich wird jede Hilfe in die richtige Richtung geschätzt, aber mein Ziel ist es, diesen Vergleich so weit wie möglich zu optimieren.

+0

Dies ist ein one-to-many-Vergleichsprozess oder many-to-many? – mjolinor

+0

@mjolinor eins-zu-viele; Ein Hash wie $ crit, verglichen mit vielen Arrays wie $ myarr. –

Antwort

0

Konvertieren Sie zuerst die Hashes in Objekte.Ich habe zwei mögliche Optionen enthalten

$myarr = @(
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"}, 
    @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"}, 
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"} 
)|ForEach-Object {New-Object -TypeName psobject -Property $_} 

# or 

$myarr = @(
    [pscustomobject]@{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"}, 
    [pscustomobject]@{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"}, 
    [pscustomobject]@{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"} 
) 

starten Sie die Sammlung Filterung

  1. kopieren den vollständigen Satz in $result
  2. Schleife über jede Bedingung
    1. Verwenden Where-Object unter dieser Bedingung filtern
    2. Speichern Sie das gefilterte Ergebnis für die nächste in $result Schleife

Code wie folgt aussieht

$crit = @{"first"="A";"third"="C"} 

$result=$myarr 
$crit.GetEnumerator()|ForEach-Object { 
    $result=$result|Where-Object -Property $_.Name -EQ $_.Value 
} 
$result 

Ausgang ist

first : A 
second : B 
third : C 
fourth : D 
fifth : E 

first : A 
second : B 
third : C 
fourth : D 
fifth : Z 
+0

Ich habe diese Lösung getestet, bis jetzt für meine Zwecke, es ist die beste Lösung. Meine Testfälle funktionierten gut, aber als ich eine Menge realer Daten hinzufügte, endete ich mit den richtigen Ergebnissen plus einer TON von 'Argument kann nicht validiert werden für Parameter 'Eigenschaft'. Das Argument ist null oder leer. "Fehler ... Ich versuche herauszufinden, warum genau das der Fall ist, und sobald ich den Schuldigen herausgefiltert habe, werde ich ein paar Benchmarks davon bekommen gegenüber meiner Brute-Force-Iteration. Sobald ich mehr Ergebnisse habe, werde ich das wahrscheinlich als die beste Antwort bezeichnen. –

+0

Mir ist auch aufgefallen, dass Ihre Daten in Bezug auf Schlüssel und die Eigenschaften, die auf meiner Lösung basieren, inkonsistent sind. Das 'where-object' ist derjenige, der den Fehler auslöst, wenn der Kriterienschlüssel nicht als Schlüssel des Hashes existiert, der zu einer Eigenschaft wurde. Ich erwarte keine besonders hohen Leistungszahlen, besonders im Vergleich zu Brute Force. Die Verrohrung wird die Vorgänge verlangsamen, auch wenn Sie die Anfangszeit der Umwandlung der Hashes in Objekte nicht zählen. Kleine Frage, woher kommen Ihre Daten und generieren so viele Hashes, dass die Leistung von Bedeutung ist? –

+0

Ich habe tatsächlich Szenarien ausprobiert, wo der Kriterienschlüssel nicht existiert, und es hat den Fehler nicht geworfen, das war einer meiner ersten Gedanken auch. Diese Daten stammen aus einer Sammlung von Millionen von Textdateien. Die Dateien haben eine Größe von einigen KB bis knapp 1 GB und sind inkonsistent begrenzt. Was ich damit erreichen will, ist das Ersetzen einer neuen Regex für jede Abfrage dieser Dateien ersetzen. Die Leistung wird aufgrund des Volumens zu einem Problem, aber es können auch so viele Threads/Jobs wie möglich gleichzeitig ausgeführt werden. –

0

Gerät eine grundlegende Funktion, um Ihre Kriterien gegen eine Hashtabelle zu testen, dann verwenden Sie Where-Object, um das Array von Hash-Tabellen zu filtern.

Für den ersten Teil haben wir so etwas wie diese

function Compare-HashtableSubset 
{ 
    param(
    [Parameter(Mandatory,Position=0)] 
    [hashtable]$HashTable, 

    [Parameter(Mandatory,Position=1)] 
    [hashtable]$SubTable 
) 

    foreach($entry in $SubTable.GetEnumerator()) { 
    if((-not $HashTable.ContainsKey($entry.Key)) -or $HashTable[$entry.Key] -ne $entry.Value){ 
     # missing key or value mismatch, we're done here 
     return $false 
    } 
    } 
    # made it to the end, must be good 
    return $true 
} 

Jetzt tun könnten, dass wir zwei Hash-Tabellen vergleichen können, lassen sie es zu benutzen!

PS C:\> $filteredArray = $myarr |Where-Object { Compare-HashtableSubset $_ $crit } 
PS C:\> $filteredArray.Count 
2 
0

Sie nicht wissen, ob dies oder nicht hilft, aber man kann es gegen einen Testsatz laufen und sehen, ob es nicht besser skaliert als Brute-Force-Iteration:

$myarr = @(
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"}, 
    @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"}, 
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"}) 

$crit = @{"first"="A";"third"="C"} 

$match1 = '*"first": "A"*' 
$match2 = '*"third": "C"*' 

($myarr |% {$_ | convertto-json}) -like $match1 -like $match2 | convertfrom-json 

Sie können oder brauche das letzte convertfrom-json nicht. Es sollte schneller ohne es laufen, wenn das Ergebnis als JSON akzeptabel ist. Es wird mehr Speicher als die Brute-Force-Iteration verwenden, sollte aber ein gesamtes Array auf einmal statt einer Hash-Tabelle gleichzeitig ausführen.

+0

Ich mag diese Idee sehr, aber für meine speziellen Zwecke skaliert sie nicht gut. Mit der Anzahl der Spiele, die ich im realen Szenario machen muss, wären ähnliche Aussagen für mich zu umständlich. –

Verwandte Themen