Dada uma matriz, um único elemento ou nil, obtenha uma matriz - os dois últimos sendo uma matriz de elemento único e uma matriz vazia, respectivamente.
Achei por engano que Ruby funcionaria desta forma:
[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
Mas o que você realmente consegue é:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Então, para resolver isso, eu preciso usar outro método ou poderia meta-programar modificando o método to_a de todas as classes que pretendo usar - o que não é uma opção para mim.
Portanto, é um método:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
O problema é que é uma bagunça. Existe uma maneira elegante de fazer isso? (Eu ficaria surpreso se esta for a maneira Ruby de resolver este problema)
Quais aplicativos isso tem? Por que até mesmo converter para um array?
No ActiveRecord do Rails, chamar, digamos, user.posts
retornará uma matriz de postagens, uma única postagem ou nil. Ao escrever métodos que funcionam nos resultados disso, é mais fácil assumir que o método terá um array, que pode ter zero, um ou muitos elementos. Método de exemplo:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
user.posts
nunca deve retornar uma única postagem. Pelo menos, eu nunca vi isso.==
vez de=
, certo?[1,2,3].to_a
se não voltar[[1,2,3]]
! Ele retorna[1,2,3]
.Respostas:
[*foo]
ouArray(foo)
funcionará na maioria das vezes, mas em alguns casos, como um hash, bagunça tudo.A única maneira de pensar que funciona mesmo para um hash é definir um método.
fonte
ensure_array
estenderto_a
to_a
. Por exemplo,{a: 1, b: 2}.each ...
funcionaria de forma diferente.Com ActiveSupport (Rails):
Array.wrap
Se você não estiver usando Rails, você pode definir seu próprio método semelhante ao fonte do Rails .
fonte
class Array; singleton_class.send(:alias_method, :hug, :wrap); end
para fofura extra.A solução mais simples é usar
[foo].flatten(1)
. Ao contrário de outras soluções propostas, funcionará bem para arrays (aninhados), hashes enil
:fonte
Kernel#Array
ou seja,Array()
é o mais rápido de todos eles. Comparação Ruby 2.5.1: Array (): 7936825,7 i / s. Array.wrap: 4199036,2 i / s - 1,89x mais lento. wrap: 644030,4 i / s - 12,32x mais lentoArray(whatever)
deve fazer o truquefonte
ActiveSupport (Rails)
ActiveSupport tem um método muito bom para isso. É carregado com Rails, então, definitivamente, a maneira mais agradável de fazer isso:
Splat (Ruby 1.9+)
O operador splat (
*
) remove a matriz de uma matriz, se puder:Obviamente, sem um array, ele faz coisas estranhas, e os objetos que você "splat" precisam ser colocados em arrays. É um pouco estranho, mas significa:
Se você não tem ActiveSupport, pode definir o método:
Embora, se você planeja ter matrizes grandes, e menos coisas que não sejam matrizes, você pode querer alterá-lo - o método acima é lento com matrizes grandes e pode até causar o estouro de seu Stack (omg so meta). De qualquer forma, você pode querer fazer isso:
Também tenho alguns benchmarks com e sem o operador teneray.
fonte
SystemStackError: stack level too deep
para elementos 1M (rubi 2.2.3).Hash#values_at
com 1 milhão de argumentos (usandosplat
) e gerou o mesmo erro.object.is_a? Array ? object : [*object]
?Array.wrap(nil)
[]
não retornanil
: /E se
fonte
[].push(anything).flatten(1)
trabalharia! Ele não nivela arrays aninhados!Com o risco de afirmar o óbvio e sabendo que este não é o açúcar sintático mais saboroso já visto no planeta e arredores, este código parece fazer exatamente o que você descreve:
fonte
você pode sobrescrever o método de array de Object
tudo herda Object, portanto to_a agora será definido para tudo sob o sol
fonte
Eu analisei todas as respostas e a maioria não funciona no Ruby 2+
Mas elado tem a solução mais elegante, ou seja,
Infelizmente, mas isso também não funciona para ruby 2+, pois você receberá um erro
Então, para consertar isso, você precisa exigir.
fonte
Como o método
#to_a
já existe para as duas principais classes problemáticas (Nil
eHash
), basta definir um método para o resto estendendoObject
:e então você pode facilmente chamar esse método em qualquer objeto:
fonte