Diberikan sebuah array, satu elemen, atau nil, dapatkan sebuah array - dua yang terakhir masing-masing menjadi array elemen tunggal dan sebuah array kosong.
Saya salah mengira Ruby akan bekerja seperti ini:
[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
Tapi yang sebenarnya Anda dapatkan adalah:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
Jadi untuk mengatasi ini, saya perlu menggunakan metode lain, atau saya bisa meta program dengan memodifikasi metode to_a dari semua kelas yang ingin saya gunakan - yang bukan merupakan pilihan bagi saya.
Jadi Metode itu adalah:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
Masalahnya adalah ini agak berantakan. Adakah cara elegan untuk melakukan ini? (Saya akan kagum jika ini adalah cara Ruby-ish untuk menyelesaikan masalah ini)
Aplikasi apa yang dimilikinya? Mengapa bahkan mengonversi ke array?
Di Rails 'ActiveRecord, memanggil say, user.posts
akan mengembalikan array posting, posting tunggal, atau nil. Saat menulis metode yang bekerja pada hasil ini, paling mudah mengasumsikan bahwa metode tersebut akan mengambil array, yang mungkin memiliki nol, satu, atau banyak elemen. Metode contoh:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
user.posts
tidak boleh mengembalikan satu pun postingan. Setidaknya, saya tidak pernah melihatnya.==
bukan=
, kan?[1,2,3].to_a
tidak tidak kembali[[1,2,3]]
! Ia kembali[1,2,3]
.Jawaban:
[*foo]
atauArray(foo)
akan bekerja di sebagian besar waktu, tetapi untuk beberapa kasus seperti hash, itu mengacaukannya.Satu-satunya cara yang dapat saya pikirkan yang berfungsi bahkan untuk hash adalah dengan menentukan metode.
sumber
ensure_array
, perpanjangto_a
to_a
. Misalnya,{a: 1, b: 2}.each ...
akan bekerja secara berbeda.Dengan ActiveSupport (Rails):
Array.wrap
Jika Anda tidak menggunakan Rails, Anda dapat menentukan metode Anda sendiri yang mirip dengan sumber Rails .
sumber
class Array; singleton_class.send(:alias_method, :hug, :wrap); end
untuk kelucuan ekstra.Solusi paling sederhana adalah menggunakan
[foo].flatten(1)
. Tidak seperti solusi lain yang diusulkan, ini akan bekerja dengan baik untuk array (nested), hashes dannil
:sumber
Kernel#Array
yaituArray()
yang tercepat dari semuanya. Ruby 2.5.1 perbandingan: Array (): 7936825.7 i / s. Array.wrap: 4199036.2 i / s - 1.89x lebih lambat. bungkus: 644030,4 i / s - 12,32x lebih lambatArray(whatever)
harus melakukan triknyasumber
ActiveSupport (Rails)
ActiveSupport memiliki metode yang cukup bagus untuk ini. Itu sarat dengan Rails, jadi cara terbaik untuk melakukan ini:
Gambar percikan (Ruby 1.9+)
Operator percikan (
*
) menghapus array, jika bisa:Tentu saja, tanpa sebuah larik, ia melakukan hal-hal aneh, dan objek yang Anda "percikan" perlu diletakkan dalam larik. Ini agak aneh, tapi artinya:
Jika Anda tidak memiliki ActiveSupport, Anda dapat menentukan metode:
Meskipun, jika Anda berencana untuk memiliki array besar, dan lebih sedikit hal-hal non-array, Anda mungkin ingin mengubahnya - metode di atas lambat dengan array besar, dan bahkan dapat menyebabkan Stack Anda Melimpah (omg so meta). Bagaimanapun, Anda mungkin ingin melakukan ini sebagai gantinya:
Saya juga memiliki beberapa benchmark dengan dan tanpa operator hari ini.
sumber
SystemStackError: stack level too deep
untuk elemen 1 juta (ruby 2.2.3).Hash#values_at
dengan argumen 1M (menggunakansplat
), dan itu membuat kesalahan yang sama.object.is_a? Array ? object : [*object]
?Array.wrap(nil)
kembali[]
tidaknil
: /Bagaimana tentang
sumber
[].push(anything).flatten(1)
akan berhasil! Itu tidak meratakan array bersarang!Dengan risiko menyatakan hal yang sudah jelas, dan mengetahui bahwa ini bukan gula sintaksis paling enak yang pernah ada di planet ini dan sekitarnya, kode ini tampaknya melakukan persis seperti yang Anda gambarkan:
sumber
Anda dapat menimpa metode array dari Object
semuanya mewarisi Object, oleh karena itu to_a sekarang akan didefinisikan untuk semua yang ada di bawah matahari
sumber
Saya telah memeriksa semua jawaban dan sebagian besar tidak berfungsi di ruby 2+
Tapi elado memiliki solusi paling elegan yaitu
Sayangnya tetapi ini juga tidak berfungsi untuk ruby 2+ karena Anda akan mendapatkan kesalahan
Jadi untuk memperbaikinya Anda perlu membutuhkan.
sumber
Karena metode
#to_a
sudah ada untuk dua kelas bermasalah utama (Nil
danHash
), cukup tentukan metode untuk sisanya dengan memperluasObject
:lalu Anda dapat dengan mudah memanggil metode itu pada objek apa pun:
sumber