Biorąc pod uwagę tablicę, pojedynczy element lub nil, uzyskaj tablicę - dwie ostatnie są odpowiednio tablicą jednoelementową i pustą.
Pomyłkowo pomyślałem, że Ruby będzie działać w ten sposób:
[1,2,3].to_a #= [1,2,3] # Already an array, so no change
1.to_a #= [1] # Creates an array and adds element
nil.to_a #= [] # Creates empty array
Ale tak naprawdę otrzymujesz:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Aby rozwiązać ten problem, muszę albo użyć innej metody, albo metaprogram, modyfikując metodę to_a wszystkich klas, których zamierzam użyć - co nie jest dla mnie opcją.
Oto metoda:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
Problem w tym, że jest trochę bałaganu. Czy jest na to elegancki sposób? (Byłbym zdziwiony, gdyby to był Rubinowy sposób rozwiązania tego problemu)
Jakie to ma aplikacje? Po co w ogóle konwertować na tablicę?
W ActiveRecord Railsów, wywołanie say, user.posts
zwróci albo tablicę postów, pojedynczy post, albo zero. Pisząc metody, które działają na wynikach tego, najłatwiej jest założyć, że metoda przyjmie tablicę, która może mieć zero, jeden lub wiele elementów. Przykładowa metoda:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
user.posts
nigdy nie powinien zwracać ani jednej wiadomości. Przynajmniej nigdy tego nie widziałem.==
zamiast=
, prawda?[1,2,3].to_a
czy nie wrócić[[1,2,3]]
! Wraca[1,2,3]
.Odpowiedzi:
[*foo]
lubArray(foo)
będzie działać przez większość czasu, ale w niektórych przypadkach, takich jak hash, psuje.Jedynym sposobem, w jaki mogę pomyśleć, że działa to nawet w przypadku skrótu, jest zdefiniowanie metody.
źródło
ensure_array
przedłużyćto_a
to_a
. Na przykład{a: 1, b: 2}.each ...
działałoby inaczej.Z ActiveSupport (Rails):
Array.wrap
Jeśli nie używasz Railsów, możesz zdefiniować własną metodę podobną do źródła railsów .
źródło
class Array; singleton_class.send(:alias_method, :hug, :wrap); end
dla dodatkowej urody.Najprostszym rozwiązaniem jest użycie
[foo].flatten(1)
. W przeciwieństwie do innych proponowanych rozwiązań sprawdzi się dobrze w przypadku (zagnieżdżonych) tablic, skrótów inil
:źródło
Kernel#Array
ieArray()
jest najszybszym z nich wszystkich. Porównanie z Rubim 2.5.1: Array (): 7936825,7 i / s. Array.wrap: 4199036,2 i / s - 1,89x wolniej. owinięcie: 644030.4 i / s - 12,32x wolniejArray(whatever)
powinien załatwić sprawęźródło
ActiveSupport (szyny)
ActiveSupport ma na to całkiem niezłą metodę. Jest załadowany Railsami, więc zdecydowanie najładniejszy sposób na zrobienie tego:
Splat (Ruby 1.9+)
Operator splat (
*
) usuwa tablice z tablicy, jeśli może:Oczywiście bez tablicy robi dziwne rzeczy, a obiekty, które „splata” trzeba umieścić w tablicach. To trochę dziwne, ale oznacza:
Jeśli nie masz ActiveSupport, możesz zdefiniować metodę:
Chociaż, jeśli planujesz mieć duże tablice i mniej rzeczy niezwiązanych z tablicami, możesz chcieć to zmienić - powyższa metoda jest powolna w przypadku dużych tablic i może nawet spowodować przepełnienie stosu (omg, więc meta). W każdym razie możesz to zrobić zamiast tego:
Mam też kilka testów z operatorem dzisiejszego dnia i bez niego.
źródło
SystemStackError: stack level too deep
dla 1M elementów (ruby 2.2.3).Hash#values_at
z 1 mln argumentów (używającsplat
) i zgłasza ten sam błąd.object.is_a? Array ? object : [*object]
?Array.wrap(nil)
[]
nie zwracanil
: /Co powiesz na
źródło
[].push(anything).flatten(1)
pracowałbym! Nie spłaszcza tablic zagnieżdżonych!Ryzykując stwierdzenie oczywistego i wiedząc, że nie jest to najsmaczniejszy cukier syntaktyczny, jaki kiedykolwiek widziano na naszej planecie i otaczających ją obszarach, ten kod wydaje się robić dokładnie to, co opisujesz:
źródło
możesz nadpisać metodę tablicową Object
wszystko dziedziczy Object, dlatego to_a będzie teraz zdefiniowane dla wszystkiego pod słońcem
źródło
Przejrzałem wszystkie odpowiedzi i przeważnie nie działają w Ruby 2+
Ale elado ma najbardziej eleganckie rozwiązanie, tj
Niestety, ale to również nie działa dla Ruby 2+, ponieważ pojawi się błąd
Aby to naprawić, musisz wymagać.
źródło
Ponieważ metoda
#to_a
już istnieje dla dwóch głównych klas problematycznych (Nil
iHash
), po prostu zdefiniuj metodę dla pozostałych, rozszerzającObject
:a następnie możesz łatwo wywołać tę metodę na dowolnym obiekcie:
źródło