@JanDvorak Pertanyaan ini tidak hanya tentang mengembalikan subhash tetapi juga tentang mengubah yang sudah ada. Hal yang sangat mirip tetapi ActiveSupport memiliki cara berbeda untuk mengatasinya.
skalee
Jawaban:
59
Jika Anda secara khusus ingin metode untuk mengembalikan elemen yang diekstrak tetapi h1 tetap sama:
Ini adalah O (n2) - Anda akan memiliki satu loop pada seleksi, loop lain pada include yang akan dipanggil h1.size times.
metakungfu
1
Meskipun jawaban ini layak untuk ruby murni, jika Anda menggunakan rel, jawaban di bawah ini (menggunakan sliceexcept
bawaan
138
ActiveSupport, Setidaknya sejak 2.3.8, menyediakan empat metode yang mudah: #slice, #exceptdan rekan-rekan mereka yang merusak: #slice!dan #except!. Mereka disebutkan dalam jawaban lain, tetapi untuk menjumlahkannya di satu tempat:
Perhatikan nilai kembalian dari metode bang. Mereka tidak hanya akan menyesuaikan hash yang ada tetapi juga mengembalikan entri yang dihapus (tidak disimpan). Yang Hash#except!paling cocok dengan contoh yang diberikan dalam pertanyaan:
x ={a:1, b:2, c:3, d:4}# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.except!(:c,:d)# => {:a=>1, :b=>2}
x
# => {:a=>1, :b=>2}
ActiveSupporttidak membutuhkan seluruh Rails, cukup ringan. Faktanya, banyak permata non-rel bergantung padanya, jadi kemungkinan besar Anda sudah memilikinya di Gemfile.lock. Tidak perlu memperluas kelas Hash Anda sendiri.
Penambalan mokey jelas merupakan cara untuk masuk IMO. Jauh lebih bersih dan membuat maksudnya lebih jelas.
Romário
1
Tambahkan untuk mengubah kode ke alamat modul inti yang benar, tentukan modul dan impor perluasan Hash core ... modul CoreExtensions modul Hash def slice (* keys) :: Hash [[keys, self.values_at (* keys)]. Transpose] end end akhiri Hash.include CoreExtensions :: Hash
Saya pikir Anda sedang mendeskripsikan ekstrak !. ekstrak! menghapus kunci dari hash awal, mengembalikan hash baru yang berisi kunci yang dihapus. mengiris! melakukan hal sebaliknya: hapus semua kecuali kunci yang ditentukan dari hash awal (sekali lagi, mengembalikan hash baru yang berisi kunci yang dihapus). Sangat bagus! sedikit lebih seperti operasi "pertahankan".
Pekerjaan yang baik. Tidak seperti yang dia minta. Metode Anda mengembalikan: {: d =>: D,: b =>: B,: e => nil,: f => nil} {: c =>: C,: a =>: A,: d => : D,: b =>: B}
Andy
Solusi satu baris yang setara (dan mungkin lebih cepat): <pre> def subhash(*keys) select {|k,v| keys.include?(k)} end
Berikut adalah solusi fungsional yang dapat berguna jika Anda tidak menjalankan Ruby 2.5 dan jika Anda tidak ingin mencemari kelas Hash Anda dengan menambahkan metode baru:
Jawaban:
Jika Anda secara khusus ingin metode untuk mengembalikan elemen yang diekstrak tetapi h1 tetap sama:
Dan jika Anda ingin menambalnya ke dalam kelas Hash:
Jika Anda hanya ingin menghapus elemen tertentu dari hash, itu jauh lebih mudah menggunakan delete_if .
sumber
slice
except
ActiveSupport
, Setidaknya sejak 2.3.8, menyediakan empat metode yang mudah:#slice
,#except
dan rekan-rekan mereka yang merusak:#slice!
dan#except!
. Mereka disebutkan dalam jawaban lain, tetapi untuk menjumlahkannya di satu tempat:Perhatikan nilai kembalian dari metode bang. Mereka tidak hanya akan menyesuaikan hash yang ada tetapi juga mengembalikan entri yang dihapus (tidak disimpan). Yang
Hash#except!
paling cocok dengan contoh yang diberikan dalam pertanyaan:ActiveSupport
tidak membutuhkan seluruh Rails, cukup ringan. Faktanya, banyak permata non-rel bergantung padanya, jadi kemungkinan besar Anda sudah memilikinya di Gemfile.lock. Tidak perlu memperluas kelas Hash Anda sendiri.sumber
x.except!(:c, :d)
(dengan bang) seharusnya# => {:a=>1, :b=>2}
. Bagus jika Anda bisa mengedit jawaban Anda.Jika Anda menggunakan rel , hash # slice adalah cara yang tepat.
Jika Anda tidak menggunakan rails , Hash # values_at akan mengembalikan nilai dalam urutan yang sama seperti yang Anda minta sehingga Anda dapat melakukan ini:
ex:
Penjelasan:
Dari yang
{:a => 1, :b => 2, :c => 3}
kita inginkan{:a => 1, :b => 2}
Jika Anda merasa seperti menambal monyet adalah cara yang harus dilakukan, berikut ini yang Anda inginkan:
sumber
Ruby 2.5 menambahkan Hash # slice :
sumber
Anda dapat menggunakan slice! (* Kunci) yang tersedia di ekstensi inti ActiveSupport
initial_hash sekarang akan menjadi
extracted_slide sekarang akan menjadi
Anda bisa melihat
slice.rb in ActiveSupport 3.1.3
sumber
sumber
def subhash(*keys) select {|k,v| keys.include?(k)} end
sumber
jika Anda menggunakan rel, mungkin lebih mudah menggunakan Hash.except
sumber
sumber
Berikut adalah perbandingan kinerja cepat dari metode yang disarankan,
#select
sepertinya yang tercepatPerbaikan akan terlihat seperti ini:
Dan untuk menggunakannya:
sumber
Keduanya
delete_if
dankeep_if
merupakan bagian dari inti Ruby. Di sini Anda dapat mencapai apa yang Anda inginkan tanpa menambalHash
jenisnya.Untuk info lebih lanjut, periksa tautan di bawah dari dokumentasi:
sumber
Seperti yang disebutkan orang lain, Ruby 2.5 menambahkan metode Hash # slice.
Rails 5.2.0beta1 juga menambahkan versi Hash # slice miliknya sendiri untuk memberikan fungsionalitas bagi pengguna framework yang menggunakan versi Ruby sebelumnya. https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8
Jika ingin menerapkan milik Anda sendiri karena alasan apa pun, itu juga bagus:
sumber
Kode ini memasukkan fungsionalitas yang Anda minta ke dalam kelas Hash:
dan memberikan hasil yang Anda berikan:
Catatan: metode ini sebenarnya mengembalikan kunci / nilai yang diekstrak.
sumber
Berikut adalah solusi fungsional yang dapat berguna jika Anda tidak menjalankan Ruby 2.5 dan jika Anda tidak ingin mencemari kelas Hash Anda dengan menambahkan metode baru:
Kemudian Anda dapat menerapkannya bahkan pada hash bersarang:
sumber
Hanya tambahan untuk metode slice, jika kunci subhash yang ingin Anda pisahkan dari hash asli akan menjadi dinamis, Anda dapat melakukannya,
sumber
Kita dapat melakukannya dengan mengulang pada kunci yang ingin kita ekstrak dan hanya memeriksa apakah kunci tersebut ada dan kemudian mengekstraknya.
sumber