Angenommen, ich habe ein assoziatives Array in bash
,
declare -A hash
hash=(
["foo"]=aa
["bar"]=bb
["baz"]=aa
["quux"]=bb
["wibble"]=cc
["wobble"]=aa
)
wo mir sowohl Schlüssel als auch Werte unbekannt sind (die tatsächlichen Daten werden aus externen Quellen gelesen).
Wie kann ich ein Array der Schlüssel erstellen, die demselben Wert entsprechen, damit ich dies in einer Schleife über alle eindeutigen Werte tun kann?
printf 'Value "%s" is present with the following keys: %s\n' "$value" "${keys[*]}"
und erhalten Sie die Ausgabe (nicht unbedingt in dieser Reihenfolge)
Value "aa" is present with the following keys: foo baz wobble
Value "bb" is present with the following keys: bar quux
Value "cc" is present with the following keys: wibble
Das wichtige Bit ist, dass die Schlüssel als separate Elemente im keys
Array gespeichert sind und daher nicht aus einer Textzeichenfolge analysiert werden müssen.
Ich könnte so etwas tun
declare -A seen
seen=()
for value in "${hash[@]}"; do
if [ -n "${seen[$value]}" ]; then
continue
fi
keys=()
for key in "${!hash[@]}"; do
if [ "${hash[$key]}" = "$value" ]; then
keys+=( "$key" )
fi
done
printf 'Value "%s" is present with the following keys: %s\n' \
"$value" "${keys[*]}"
seen[$value]=1
done
Aber es scheint ein bisschen ineffizient mit dieser Doppelschleife.
Gibt es eine Array-Syntax, die ich verpasst habe bash
?
Würde zsh
mir dies beispielsweise Zugriff auf leistungsfähigere Array-Manipulationswerkzeuge geben?
In Perl würde ich tun
my %hash = (
'foo' => 'aa',
'bar' => 'bb',
'baz' => 'aa',
'quux' => 'bb',
'wibble' => 'cc',
'wobble' => 'aa'
);
my %keys;
while ( my ( $key, $value ) = each(%hash) ) {
push( @{ $keys{$value} }, $key );
}
foreach my $value ( keys(%keys) ) {
printf( "Value \"%s\" is present with the following keys: %s\n",
$value, join( " ", @{ $keys{$value} } ) );
}
Aber bash
assoziative Arrays können keine Arrays enthalten ...
Ich würde mich auch für eine Old-School-Lösung interessieren, die möglicherweise eine Form der indirekten Indizierung verwendet (Erstellen einer Reihe von Indexarrays beim Lesen der Werte, die ich hash
oben angegeben habe?). Es scheint, dass es einen Weg geben sollte, dies in linearer Zeit zu tun.
quelle
seen
(im Echo) sollte seinelements
. Ich nehme jedoch an, dass jedes $ {Seen [$ k]} eine Zeichenfolge mit durch Leerzeichen getrennten Elementen ist, für die daher eine zusätzliche Analyse erforderlich ist, die das OP nicht wollte (?)Wie Sie sicher wissen, besteht der Stolperstein darin, den gesamten Wert eines indizierten Arrays abzurufen, wenn sein Name als Wert einer (anderen) Variablen angegeben wird. Ich könnte es nicht mit weniger tun, als ein Zwischenprodukt zu haben, dessen Wert formatiert wird,
${v[@]}
und dann eval dafür verwenden. Also, hier ist dieser Ansatz:Dies ist für Linux
bash
. Es schafft indizierte ArraysIX1
,IX2
usw., für die verschiedenen Werte trifft es auf und hält diese Namen in diekeys
assoziative Array für die Werte. Dies${keys[$value]}
ist also der Name des indizierten Arrays, das die Schlüssel für diesen Wert enthält. DannX
wird die Variable "Zugriffsphrase" für die Sammlung von Werten eingerichtet, dieeval echo "$X"
die Übersetzung in diese Werte mit Leerzeichen ermöglicht. Wenn ein Wert beispielsweise ein Array indiziert hatIX2
, istX
dies die Zeichenfolge${IX2[@]}
.Ich glaube, dass
zsh
es ähnlich ist, Arrays von Arrays nicht zu unterstützen, daher würde es wahrscheinlich eine ähnliche Lösung erfordern. IMHO sind die Zugriffsphrasen inzsh
jedoch etwas klarer.quelle