W Rubim, mając tablicę w jednej z następujących form ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... jaki jest najlepszy sposób na przekształcenie tego w hash w postaci ...
{apple => 1, banana => 2}
UWAGA : Aby uzyskać zwięzłe i wydajne rozwiązanie, zapoznaj się z odpowiedzią Marca-André Lafortune'a poniżej.
Ta odpowiedź była pierwotnie oferowana jako alternatywa dla podejść wykorzystujących spłaszczenie, które były najbardziej opiniotwórcze w momencie pisania. Powinienem był wyjaśnić, że nie zamierzałem przedstawiać tego przykładu jako najlepszej praktyki lub skutecznego podejścia. Następuje oryginalna odpowiedź.
Ostrzeżenie! Rozwiązania wykorzystujące spłaszczenie nie zachowują kluczy ani wartości tablicy!
Opierając się na popularnej odpowiedzi @John Topley, spróbujmy:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Powoduje to wyświetlenie błędu:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
Konstruktor oczekiwał tablicy o parzystej długości (np. ['K1', 'v1,' k2 ',' v2 ']). Co gorsza, inna tablica, która spłaszczyłaby się do równej długości, po cichu dałby nam Hash z nieprawidłowymi wartościami.
Jeśli chcesz użyć kluczy lub wartości tablicy, możesz użyć map :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
To zachowuje klucz Array:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
zamiast tegoh3 = Hash[*a3.flatten]
zgłosiłby błąd.to_h
jest lepszy.Po prostu użyj
Hash[*array_variable.flatten]
Na przykład:
Użycie
Array#flatten(1)
ogranicza rekursję, abyArray
klucze i wartości działały zgodnie z oczekiwaniami.źródło
Hash[*ary.flatten(1)]
, która zachowa klucze i wartości tablic. To rekurencjaflatten
niszczy te, których łatwo jest uniknąć.Najlepszym sposobem jest użycie
Array#to_h
:Zauważ, że
to_h
akceptuje również blok:Uwaga :
to_h
akceptuje blok w Rubim 2.6.0+; za wczesne rubiny możesz użyć mojegobackports
klejnotu irequire 'backports/2.6.0/enumerable/to_h'
to_h
bez bloku został wprowadzony w Rubim 2.1.0.Przed Ruby 2.1 można było używać mniej czytelnych
Hash[]
:Wreszcie, uważaj na jakiekolwiek rozwiązania wykorzystujące
flatten
, może to spowodować problemy z wartościami, które same są tablicami.źródło
to_h
Metoda podoba mi się bardziej niż powyższe odpowiedzi, ponieważ wyraża zamiar konwersji po operacji na tablicy.Array#to_h
też nieEnumerable#to_h
jest w rdzeniu Ruby 1.9.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
i chcę, aby dane wyjściowe były{"apple" =>[1,3], "banana"=>[2,4]}
?Aktualizacja
Dzisiaj został wydany Ruby 2.1.0 . I przychodzę z
Array#to_h
( uwagi do wydania i ruby-doc ), co rozwiązuje problem konwersji pliku anArray
na plikHash
.Przykład dokumentacji Ruby:
źródło
Druga forma jest prostsza:
a = tablica, h = hash, r = hash wartości zwracanej (ten, w którym gromadzimy), i = element w tablicy
Najładniejszy sposób, w jaki przychodzi mi do głowy wykonanie pierwszej formy, to coś takiego:
źródło
a.inject({})
jedną linijkę, która pozwala na bardziej elastyczne przypisywanie wartości.h = {}
z drugiego przykładu przez użycie inject, kończąc naa.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
Możesz także po prostu przekonwertować tablicę 2D na hash za pomocą:
źródło
Podsumowanie i TL; DR:
Mam nadzieję, że ta odpowiedź będzie wyczerpującym podsumowaniem informacji uzyskanych z innych odpowiedzi.
Bardzo krótka wersja, biorąc pod uwagę dane z pytania oraz kilka dodatków:
Następuje dyskusja i szczegóły.
Konfiguracja: zmienne
Aby z góry pokazać dane, których będziemy używać, utworzę zmienne reprezentujące różne możliwości danych. Pasują do następujących kategorii:
Na podstawie tego, co było bezpośrednio w pytaniu, jak
a1
ia2
:(Uwaga: zakładam, że tak jest
apple
ibanana
miały one reprezentować zmienne. Podobnie jak inni, odtąd będę używać ciągów znaków, aby dane wejściowe i wyniki mogły się zgadzać).Klucze i / lub wartości wielowartościowe, jak
a3
:W niektórych innych odpowiedziach przedstawiono inną możliwość (którą tutaj rozwijam) - klucze i / lub wartości mogą same być tablicami:
Niezbalansowana tablica, jak
a4
:Pomyślałem, że dodam jeden w przypadku, gdy możemy mieć niepełne dane wejściowe:
A teraz do pracy:
Zaczynając od początkowo płaskiej tablicy
a1
:Niektórzy sugerowali użycie
#to_h
(które pojawiło się w Ruby 2.1.0 i może zostać przeniesione do wcześniejszych wersji). W przypadku początkowo płaskiej tablicy to nie działa:Używanie w
Hash::[]
połączeniu z operatorem splat powoduje:To jest rozwiązanie dla prostego przypadku reprezentowanego przez
a1
.Z tablicą tablic par klucz / wartość
a2
:W przypadku tablicy
[key,value]
tablic typów istnieją dwa sposoby.Po pierwsze,
Hash::[]
nadal działa (tak jak w przypadku*a1
):A potem
#to_h
działa również teraz:A więc dwie proste odpowiedzi na prosty przypadek tablicy zagnieżdżonej.
Jest to prawdą nawet w przypadku tablic podrzędnych jako kluczy lub wartości, jak w przypadku
a3
:Ale duriany mają kolce (anomalne struktury powodują problemy):
Jeśli otrzymaliśmy niezbilansowane dane wejściowe, napotkamy problemy z
#to_h
:Ale
Hash::[]
nadal działa, po prostu ustawiającnil
jako wartość dladurian
(i każdy inny element tablicy w a4, który jest po prostu tablicą z 1 wartością):Spłaszczanie - przy użyciu nowych zmiennych
a5
ia6
Wspomniano kilka innych odpowiedzi
flatten
, z1
argumentami lub bez , więc stwórzmy kilka nowych zmiennych:Zdecydowałem się użyć
a4
jako bazy danych z powodu problemu z równowagą, który pojawił się za4.to_h
. Wydaje mi się,flatten
że dzwonienie może być podejściem, którego ktoś mógłby użyć, aby rozwiązać ten problem, co może wyglądać następująco.flatten
bez argumentów (a5
):W naiwnej rzut oka wydaje do pracy - ale to ma nas na niewłaściwym stóp z pestek pomarańczy, dlatego też co
3
do klucza idurian
do wartości .I to, podobnie jak w przypadku
a1
, po prostu nie działa:Więc
a4.flatten
nie jest dla nas przydatny, chcielibyśmy po prostu użyćHash[a4]
Obudowa
flatten(1)
(a6
):Ale co z tylko częściowym spłaszczeniem? Warto zauważyć, że wywołanie
Hash::[]
usingsplat
na częściowo spłaszczonej tablicy (a6
) nie jest tym samym, co wywołanieHash[a4]
:Tablica wstępnie spłaszczona, nadal zagnieżdżona (alternatywny sposób pobierania
a6
):Ale co, jeśli tak właśnie otrzymaliśmy tablicę? (Oznacza to,
a1
że były to nasze dane wejściowe - tylko tym razem niektóre dane mogą być tablicami lub innymi obiektami). Widzieliśmy, żeHash[*a6]
to nie działa, ale co by było, gdybyśmy nadal chcieli uzyskać zachowanie, w którym ostatni element (ważny! patrz poniżej) działał jako klucz dlanil
wartości?W takiej sytuacji nadal istnieje sposób, aby to zrobić, używając
Enumerable#each_slice
do powrotu do par klucz / wartość jako elementów w tablicy zewnętrznej:Zwróć uwagę, że w rezultacie otrzymujemy nową tablicę, która nie jest „ identyczna ” z
a4
, ale ma te same wartości :I tak możemy ponownie użyć
Hash::[]
:Ale jest problem!
Należy zauważyć, że
each_slice(2)
rozwiązanie przywraca normalność tylko wtedy, gdy ostatni klucz nie zawiera wartości. Jeśli później dodaliśmy dodatkową parę klucz / wartość:A dwa skróty, które otrzymamy z tego, są różne w ważnych aspektach:
(Uwaga: używam
awesome_print
„sap
. Tak aby łatwiej pokazać strukturę tutaj; nie ma koncepcyjnego wymóg ten temat)Tak więc
each_slice
rozwiązanie niezbalansowanego płaskiego wejścia działa tylko wtedy, gdy niezbalansowany bit znajduje się na samym końcu.Dania na wynos:
[key, value]
pary (podtablica dla każdego elementu w tablicy zewnętrznej).#to_h
lubHash::[]
drugie zadziała.Hash::[]
połączeniu ze splat (*
) zadziała, o ile wejścia są zbalansowane .value
element jest jedynym, którego brakuje.Na marginesie: zamieszczam tę odpowiedź, ponieważ czuję, że warto ją dodać - niektóre z istniejących odpowiedzi zawierają niepoprawne informacje, a żadna (którą przeczytałem) nie dała tak kompletnej odpowiedzi, jak staram się tutaj zrobić. Mam nadzieję, że jest to pomocne. Niemniej jednak dziękuję tym, którzy byli przede mną, a kilku z nich zainspirowało części tej odpowiedzi.
źródło
Dołączanie do odpowiedzi, ale przy użyciu anonimowych tablic i adnotacji:
Biorąc tę odpowiedź na bok, zaczynając od środka:
"a,b,c,d"
jest w rzeczywistości ciągiem.split
przecinkami do tablicy.zip
razem z następującą tablicą.[1,2,3,4]
jest rzeczywistą tablicą.Wynik pośredni to:
spłaszcz, a następnie przekształca to w:
i wtedy:
*["a",1,"b",2,"c",3,"d",4]
rozwija to do"a",1,"b",2,"c",3,"d",4
których możemy użyć jako argumentów do
Hash[]
metody:co daje:
źródło
*
) i spłaszczenia:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Więcej szczegółów w odpowiedzi, którą dodałem.jeśli masz tablicę, która wygląda tak -
i chcesz, aby pierwsze elementy każdej tablicy stały się kluczami dla skrótu, a reszta elementów stała się tablicami wartości, możesz zrobić coś takiego -
źródło
Nie jestem pewien, czy to najlepszy sposób, ale to działa:
źródło
Jeśli wartości liczbowe są indeksami sekwencyjnymi, moglibyśmy mieć prostsze sposoby ... Oto moje przesłanie kodu, Mój Ruby jest trochę zardzewiały
źródło