In Perl, wie erstelle ich einen Hash, dessen Schlüssel kommen aus einem bestimmten Array?

StackOverflow https://stackoverflow.com/questions/95820

  •  01-07-2019
  •  | 
  •  

Frage

Lassen Sie uns sagen, dass ich ein Array, und ich weiß, ich werde eine Menge zu tun „Hat das Array enthalten X?“ Schecks. Der effizienteste Weg, dies zu tun, ist das Array in einen Hash zu drehen, wo die Schlüssel sind die Array-Elemente, und dann kann man nur sagen,

if($hash{X}) { ... }

Gibt es eine einfache Möglichkeit, diese Array-to-Hash-Umwandlung zu tun? Idealerweise sollte es vielseitig genug sein, ein anonymes Array zu nehmen und einen anonymen Hash zurück.

War es hilfreich?

Lösung

%hash = map { $_ => 1 } @array;

Es ist nicht so kurz wie die „@hash {@array} = ...“ Lösungen, aber diese diejenigen erfordern den Hash und Array bereits irgendwo anders definiert werden, während dies ein ein anonymes Array übernehmen und zurückgeben eine anonyme Hash.

Was das bedeutet ist jedes Element nimmt in dem Array und koppelt es mit einem „1“ auf. Wenn diese Liste von (Schlüssel, 1, Schlüssel, 1, Taste 1) Paaren zu einem Hash zugewiesen bekommen, die ungerade nummerierten werden die Schlüssel des Hash und die geradzahlige werden die jeweiligen Werte.

Andere Tipps

 @hash{@array} = (1) x @array;

Es ist eine Hash-Scheibe, eine Liste von Werten aus dem Hash, so wird es die Liste-y @ vor.

die Dokumentation :

  

Wenn Sie sind verwirrt darüber, warum Sie verwenden   ein ‚@‘ dort auf einem Hash-slice anstelle   ein ‚%‘, denken Sie daran, wie diese. Das   Art der Halterung (quadratisch oder gekräuselt)   regelt, ob es ein Array oder eine ist   Hash wird betrachtet. Auf dem anderen   Hand, das führende Symbol ( '$' oder '@')   auf dem Array oder Hash zeigt an, ob   Sie bekommen einen einzigartigen Wert zurück   (Ein Skalar) oder eine Mehrzahl von einem (eine Liste).

@hash{@keys} = undef;

Die Syntax hier, wo Sie mit einem @ auf das Hash beziehen ist ein Hash-Slice. Wir sagen im Grunde $hash{$keys[0]} UND $hash{$keys[1]} UND $hash{$keys[2]} ... ist eine Liste auf der linken Seite des = ein L-Wertes, und wir zu dieser Liste sind die Zuordnung, die tatsächlich in die Hash geht und setzt die Werte für all genannt Schlüssel. In diesem Fall habe ich angegeben nur einen Wert, so wird dieser Wert in $hash{$keys[0]} geht, und die anderen Hash-Einträge alle Auto-vivify (zum Leben) mit undefinierten Werten. [My hier ursprünglicher Vorschlag festgelegt wurde, die Expression = 1, was, dass ein Schlüssel undef zu 1 und die andere gesetzt haben würde. Habe ich es für Konsistenz, aber wie wir weiter unten sehen werden, die genauen Werte sind nicht wichtig.]

Wenn Sie merken, dass der L-Wert, der Ausdruck auf der linken Seite der = eine Liste aus dem Hash gebaut ist, dann wird es beginnen, einig Sinn machen, warum wir diese @ verwenden. [Außer, dass ich denke, das ist in Perl 6 ändern.]

Die Idee dabei ist, dass Sie den Hash als Satz verwenden. Was zählt, ist nicht der Wert ich zuweisen; es ist nur die Existenz der Schlüssel. Also, was wollen Sie tun, ist nicht so etwas wie:

if ($hash{$key} == 1) # then key is in the hash

statt:

if (exists $hash{$key}) # then key is in the set

Es ist eigentlich effizienter nur eine exists Prüfung durchführt als mit dem Wert in der Hash zu stören, obwohl mir das Wichtigste hier nur das Konzept ist, dass Sie einen Satz nur mit den Tasten des Hash repräsentieren. Auch wies jemand darauf hin, dass hier durch die Verwendung undef als der Wert, werden wir weniger Speicherplatz verbrauchen, als wir einen Wert würde zuweisen. (Und erzeugen auch weniger Verwirrung, da der Wert spielt keine Rolle, und meine Lösung würde einen Wert zuweisen nur auf das erste Element in der Hash-und lassen Sie die anderen undef und einige andere Lösungen wenden cartwheels ein Array von Werten zu bauen gehen in die Hash-; vollständig vergebliche Mühe)

.

Beachten Sie, dass, wenn if ( exists $hash{ key } ) Eingabe nicht zu viel Arbeit für Sie ist (was ich seit der Angelegenheit von Interesse zu verwenden, bevorzugen ist wirklich das Vorhandensein eines Schlüssels statt der Iness seines Wertes), dann können Sie die kurze verwenden und süß

@hash{@key} = ();

Es ist eine Voraussetzung hier, dass der effizienteste Weg, um eine Menge zu tun, von „Hat das Array X enthalten?“ Kontrollen sind das Array auf einen Hash zu konvertieren. Effizienz ist abhängig von der knappen Ressource, oft Zeit, aber manchmal Raum und manchmal Programmierer Aufwand. Sie sind zumindest eine Verdoppelung der, indem sie eine Liste und eine Hash-Wert der in der Liste um gleichzeitig verbrauchte Speicher. Plus Sie mehr Original-Code zu schreiben, die Sie benötigen, um zu testen, Dokument, etc.

Als Alternative Blick auf die Liste :: MoreUtils Modul, insbesondere die Funktionen any(), none(), true() und false(). Sie alle nehmen einen Block als die bedingten und eine Liste als Argument, ähnlich wie map() und grep():

print "At least one value undefined" if any { !defined($_) } @list;

lief ich einen schnellen Test, Laden in der Hälfte von / usr / share / dict / words auf ein Array (25000 Wörter), dann für elf Wörtern suchen aus dem gesamten Wörterbuch ausgewählt (jedes 5000. Wort) in der Anordnung, mit sowohl das Array-to-Hash-Verfahren und die any() Funktion aus der Liste :: MoreUtils.

Auf Perl 5.8.8 aus dem Quellcode, das Array-to-Hash-Verfahren läuft fast 1100-fach schneller als die any() Methode (1300x schneller unter Ubuntu 6.06 ist verpackt Perl 5.8.7.)

Das ist nicht die ganze Geschichte aber - die Array-to-Hash-Umwandlung dauert etwa 0,04 Sekunden, was in diesem Fall die Zeiteffizienz tötet von Array-to-Hash-Verfahren auf 1,5x-2x schneller als die any() Methode. Immer noch gut, aber nicht annähernd so stellar.

Mein Bauchgefühl ist, dass das Array-to-Hash-Verfahren in den meisten Fällen schlagen any() geht, aber ich würde sehr viel besser fühlen, wenn ich noch ein paar festen Metriken (viele Testfälle, anständige statistische Analysen habe, vielleicht einige big-O algorithmische Analyse jedes Verfahren, etc.) Je nach Bedarf, List :: MoreUtils kann eine bessere Lösung sein; es ist sicherlich flexibler und erfordert weniger Codierung. Denken Sie daran, vorzeitige Optimierung ist eine Sünde ...:)

Ich habe immer gedacht, dass

foreach my $item (@array) { $hash{$item} = 1 }

war zumindest schön und gut lesbar / wartbar.

In Perl 5.10, gibt es die Nähe zu magischen ~~ Operator:

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

Sehen Sie hier: http: //dev.perl. org / perl5 / news / 2007 / perl-5.10.0.html

Auch erwähnenswert für die Vollständigkeit, meine übliche Methode hierfür mit 2 gleiche Länge Arrays tun @keys und @vals, welche Sie lieber ein Hash waren ...

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);

Raldi die Lösung dieses Problem verschärft (die ‚=>‘ aus dem Original ist nicht erforderlich) werden kann:

my %hash = map { $_,1 } @array;

Diese Technik kann auch für das Drehen von Textlisten in Hashes verwendet werden:

my %hash = map { $_,1 } split(",",$line)

Darüber hinaus, wenn Sie eine Zeile von Werten wie folgt aus: "foo = 1, bar = 2, baz = 3" Sie können dies tun:

my %hash = map { split("=",$_) } split(",",$line);

[EDIT schließen]


Eine andere Lösung angeboten (die zwei Linien nimmt) ist:

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;

Sie können auch benutzen Perl6 :: Junction .

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

Wenn Sie eine Menge von mengentheoretischen Operationen tun - können Sie auch a href verwenden <= „http://search.cpan.org/~jhi/Set-Scalar-1.22/lib/Set/Scalar.pm“ rel = "nofollow noreferrer"> Set :: Scalar oder ähnliches Modul. Dann wird $s = Set::Scalar->new( @array ) das Set für Sie bauen - und Sie können es abfragen mit:. $s->contains($m)

Sie können den Code in ein Unterprogramm platzieren, wenn Sie Ihren Namensraum nicht verschmutzen möchten.

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

Oder noch besser:

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Wenn Sie wirklich wollte ein Array Referenz zu übergeben:

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Sie können auch Tie prüfen wollen, :: IxHash , die assoziative Arrays geordnete implementiert. Das würde erlauben Sie beiden Arten von Lookups (Hash-und-Index) zu tun, auf einer Kopie Ihrer Daten.

#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

ergibt (Note wiederholt Schlüssel erhalten den Wert an der größten Position im Array - dh 8-> 2 und nicht 6)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top