arreglos - Ruby: elegantemente convierte variable a una matriz si no ya una matriz
arreglos bidimensionales en ruby (10)
ActiveSupport (Rieles)
ActiveSupport tiene un método bastante agradable para esto. Está cargado con Rails, así que desafiantemente la mejor manera de hacerlo:
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
Splat (Ruby 1.9+)
El operador splat ( *
) desinstala una matriz, si puede:
*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)
Por supuesto, sin una matriz, hace cosas raras, y los objetos que "splat" deben colocarse en matrices. Es algo raro, pero significa:
[*[1,2,3]] #=> [1, 2, 3]
[*5] #=> [5]
[*nil] #=> []
[*{meh: "meh"}] #=> [[:meh, "meh"], [:meh2, "lol"]]
Si no tiene ActiveSupport, puede definir el método:
class Array
def self.wrap(object)
[*object]
end
end
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil
Aunque, si planeas tener matrices grandes y menos cosas que no sean de matriz, es posible que quieras cambiarlo; el método anterior es lento con matrices grandes e incluso puede hacer que tu pila se desborde (omg tan meta). De todos modos, es posible que desee hacer esto en su lugar:
class Array
def self.wrap(object)
object.is_a? Array ? object : [*object]
end
end
Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> [nil]
También tengo algunos benchmarks con y sin el operador teneray.
Dada una matriz, un solo elemento, o nil, obtiene una matriz, siendo las dos últimas una matriz de un solo elemento y una matriz vacía, respectivamente.
Supuse erróneamente que Ruby funcionaría de esta manera:
[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
Pero lo que realmente obtienes es:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Entonces para resolver esto, o necesito usar otro método, o podría metaprogramar modificando el método to_a de todas las clases que pretendo usar, lo cual no es una opción para mí.
Entonces un Método es:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
El problema es que es un desastre. ¿Hay una manera elegante de hacer esto? (Me sorprendería si esta es la manera Ruby-ish para resolver este problema)
¿Qué aplicaciones tiene esto? ¿Por qué incluso convertir a una matriz?
En ActiveRecord de Rails, al llamar a decir, user.posts
devolverá una matriz de publicaciones, una sola publicación o ninguna. Al escribir métodos que funcionan con los resultados de esto, es más fácil suponer que el método tomará una matriz, que puede tener cero, uno o muchos elementos. Método de ejemplo:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
Como el método #to_a
ya existe para las dos clases problemáticas principales ( Nil
y Hash
), simplemente defina un método para el resto extendiendo Object
:
class Object
def to_a
[self]
end
end
y luego puedes llamar fácilmente a ese método en cualquier objeto:
"Hello world".to_a
# => ["Hello world"]
123.to_a
# => [123]
{a:1, b:2}.to_a
# => [[:a, 1], [:b, 2]]
nil.to_a
# => []
Con ActiveSupport (Rails): Array.wrap
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(1) # => [1]
Array.wrap(nil) # => []
Array.wrap({a: 1, b: 2}) # => [{:a=>1, :b=>2}]
Si no está utilizando Rails, puede definir su propio método similar a la fuente Rails .
class Array
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end
Con el riesgo de afirmar lo obvio, y sabiendo que este no es el azúcar sintáctico más sabroso jamás visto en el planeta y las áreas circundantes, este código parece hacer exactamente lo que usted describe:
foo = foo.is_a?(Array) ? foo : foo.nil? ? [] : [foo]
He revisado todas las respuestas y la mayoría de las veces no trabajo en ruby 2+
Pero elado tiene la solución más elegante es decir
Con ActiveSupport (Rails): Array.wrap
Array.wrap ([1, 2, 3]) # => [1, 2, 3]
Array.wrap (1) # => [1]
Array.wrap (nil) # => []
Array.wrap ({a: 1, b: 2}) # => [{: a => 1,: b => 2}]
Lamentablemente, esto tampoco funciona con ruby 2+ ya que obtendrás un error
undefined method `wrap'' for Array:Class
Entonces, para arreglar eso necesitas pedirlo.
requiere ''active_support / deprecation''
requiere ''active_support / core_ext / array / wrap''
La solución más simple es usar [foo].flatten(1)
. A diferencia de otras soluciones propuestas, funcionará bien para matrices (anidadas), hash y nil
:
def wrap(foo)
[foo].flatten(1)
end
wrap([1,2,3]) #= [1,2,3]
wrap([[1,2],[3,4]]) #= [[1,2],[3,4]]
wrap(1) #= [1]
wrap(nil) #= [nil]
wrap({key: ''value''}) #= [{key: ''value''}]
Qué tal si
[].push(anything).flatten
puedes sobreescribir el método de matriz de Object
class Object
def to_a
[self]
end
end
todo hereda Objeto, por lo tanto, to_a ahora se definirá para todo lo que esté debajo del sol
Array(whatever)
debería hacer el truco
Array([1,2,3]) # [1,2,3]
Array(nil) # []
Array(1337) # [1337]
[*foo]
o Array(foo)
funcionarán la mayor parte del tiempo, pero en algunos casos como un hash, lo estropea.
Array([1, 2, 3]) # => [1, 2, 3]
Array(1) # => [1]
Array(nil) # => []
Array({a: 1, b: 2}) # => [[:a, 1], [:b, 2]]
[*[1, 2, 3]] # => [1, 2, 3]
[*1] # => [1]
[*nil] # => []
[*{a: 1, b: 2}] # => [[:a, 1], [:b, 2]]
La única forma en que puedo pensar que funciona incluso para un hash es definir un método.
class Object; def ensure_array; [self] end end
class Array; def ensure_array; to_a end end
class NilClass; def ensure_array; to_a end end
[1, 2, 3].ensure_array # => [1, 2, 3]
1.ensure_array # => [1]
nil.ensure_array # => []
{a: 1, b: 2}.ensure_array # => [{a: 1, b: 2}]