Cara mengganti kunci hash dengan kunci lain


192

Saya memiliki kondisi di mana, saya mendapatkan hash

  hash = {"_id"=>"4de7140772f8be03da000018", .....}

dan saya ingin hash ini sebagai

  hash = {"id"=>"4de7140772f8be03da000018", ......}

PS : Saya tidak tahu apa kunci dalam hash, itu acak yang dilengkapi dengan awalan "_" untuk setiap kunci dan saya ingin tidak ada garis bawah



+1 untuk pertanyaan bermanfaat
ashisrai_

@ a5his: Saya senang itu bisa membantu :)
Manish Das

Jawaban:


711
hash[:new_key] = hash.delete :old_key

8
Menyelamatkan saya beberapa LOC, love it!
nicohvi

10
Saya sering tidak suka kode "pintar" ruby ​​karena butuh waktu untuk mengetahui apa yang sebenarnya dilakukannya. Solusi Anda di sisi lain sederhana dan deskriptif.
Lucas

3
Ini memang seharusnya jawaban yang diterima! Mudah, bersih, dan langsung ke intinya!
GigaBass

1
Jawaban ini elegan tetapi sebenarnya tidak menjawab pertanyaan. Pos menyatakan bahwa kunci yang perlu diganti tidak diketahui ... Kami hanya tahu bahwa mereka mulai dengan garis bawah, kami tidak tahu apa kunci itu sebenarnya.
Dsel

2
jadi ini menciptakan pasangan kunci / nilai baru di mana Anda menentukan kunci baru dan mendapatkan nilai dari apa yang hash.delete :old_keykembali dan penghapusan menggunakan kunci lama. WOW, saya ingin tato di suatu tempat :-D Terima kasih
Bart C

136

rails Hash memiliki metode standar untuk itu:

hash.transform_keys{ |key| key.to_s.upcase }

http://api.rubyonrails.org/classes/Hash.html#method-i-transform_keys

UPD: metode ruby ​​2.5


4
Ini metode Rails, bukan standar. Jawaban yang bagus.
user2422869

1
Juga, metode ini tidak dapat beroperasi dengan kunci hash secara rekursif.
Sergio Belevskij

5
deep_transform_keys dapat digunakan untuk itu :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat

1
Akhirnya! Inilah yang saya cari!
TiSer

4
Ini adalah bagian standar bahasa pada Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson

39

Jika semua kunci adalah string dan semuanya memiliki awalan garis bawah, maka Anda dapat menambal hash di tempat dengan ini:

h.keys.each { |k| h[k[1, k.length - 1]] = h[k]; h.delete(k) }

The k[1, k.length - 1]bit meraih semua kkecuali karakter pertama. Jika Anda ingin salinan, maka:

new_h = Hash[h.map { |k, v| [k[1, k.length - 1], v] }]

Atau

new_h = h.inject({ }) { |x, (k,v)| x[k[1, k.length - 1]] = v; x }

Anda juga dapat menggunakan subjika Anda tidak menyukai k[]notasi untuk mengekstraksi substring:

h.keys.each { |k| h[k.sub(/\A_/, '')] = h[k]; h.delete(k) }
Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

Dan, jika hanya beberapa tombol memiliki awalan garis bawah:

h.keys.each do |k|
  if(k[0,1] == '_')
    h[k[1, k.length - 1]] = h[k]
    h.delete(k)
  end
end

Modifikasi serupa dapat dilakukan untuk semua varian lain di atas tetapi dua ini:

Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

harus baik-baik saja dengan kunci yang tidak memiliki awalan garis bawah tanpa modifikasi tambahan.


jawaban Anda berhasil tetapi setelah bangsal saya menemukan beberapa hash seperti ini
Manish Das

3
{"_id" => "4de7140772f8be03da000018", "_type" => "WorkStation", "Created_at" => "2011-06-02T10: 24: 35 + 05: 45", "input_header_ids" => [], "line_id" "=>" 4de7140472f8be03da000017 "," updated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das

2
{"id" => "4de7140772f8be03da000018", "type" => "WorkStation", "reated_at" => "2011-06-02T10: 24: 35 + 05: 45", "nput_header_ids" => [], "ine_id "=>" 4de7140472f8be03da000017 "," pdated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das

@ Bahasa Spanyol: Saya memang mengatakan "Jika semua kunci adalah string dan semuanya memiliki awalan garis bawah". Saya menyertakan contoh pendekatan untuk "kunci tanpa awalan garis bawah" dalam pembaruan.
mu terlalu pendek

2
@ Bahasa Spanyol: "k" adalah untuk "kunci", "v" adalah untuk "nilai", "x" adalah untuk "Saya tidak tahu harus memanggil apa tetapi saya dilatih sebagai ahli matematika jadi saya menyebutnya x".
mu terlalu pendek

14

Anda dapat melakukan

hash.inject({}){|option, (k,v) | option["id"] = v if k == "_id"; option}

Ini seharusnya cocok untuk kasus Anda!


11

Jika kita ingin mengganti nama kunci khusus dalam hash maka kita dapat melakukannya sebagai berikut:
Misalkan hash saya my_hash = {'test' => 'ruby hash demo'}
Sekarang saya ingin mengganti 'test' dengan 'message', maka:
my_hash['message'] = my_hash.delete('test')


Bagaimana jawaban Anda kemudian solusi untuk masalah saya? Jika Anda pikir ini bermanfaat, maka Anda bisa menambahkan komentar di bawah pertanyaan. Pertanyaan saya bukan untuk mengganti kunci dengan kunci lain, solusi yang Anda berikan adalah properti Hash yang sangat mendasar. dalam kasus saya itu bukan hash[:new_key] = has[:old_key]:, bukannya itu:, itu hash[:dynamic_key] = hash[:_dynamic_key]pertanyaan yang jelas tentang regex dan bukan pengganti hash sederhana.
Manish Das

2
Saya sampai pada ini melalui pencarian google dan ingin jawaban @ Swapnil. Terima kasih
toobulkeh

10
h.inject({}) { |m, (k,v)| m[k.sub(/^_/,'')] = v; m }

4
Saya suka bahwa Anda mencoba menggunakan regex untuk menyaring garis bawah dengan benar, tetapi Anda harus menyadari bahwa di ruby, tidak seperti javascript dan lainnya, / ^ / berarti 'mulai dari string ATAU LINE', dan / $ / berarti 'akhir dari string ATAU LINE '. Tidak mungkin bahwa kunci memiliki baris baru di dalamnya dalam kasus ini, tetapi Anda harus menyadari bahwa menggunakan kedua operator di ruby ​​tidak hanya rentan kesalahan tetapi juga sangat berbahaya bila digunakan salah dalam validasi terhadap injeksi. Lihat di sini untuk penjelasan. Semoga Anda tidak keberatan menyebarkan kesadaran.
Jorn van de Beek

2
hash.each {|k,v| hash.delete(k) && hash[k[1..-1]]=v if k[0,1] == '_'}

1

Saya pergi berlebihan dan menemukan yang berikut. Motivasi saya di balik ini adalah untuk menambahkan kunci hash untuk menghindari konflik ruang lingkup ketika bergabung bersama / meratakan hash.

Contohnya

Perpanjang Kelas Hash

Menambahkan metode rekey ke instance Hash.

# Adds additional methods to Hash
class ::Hash
  # Changes the keys on a hash
  # Takes a block that passes the current key
  # Whatever the block returns becomes the new key
  # If a hash is returned for the key it will merge the current hash 
  # with the returned hash from the block. This allows for nested rekeying.
  def rekey
    self.each_with_object({}) do |(key, value), previous|
      new_key = yield(key, value)
      if new_key.is_a?(Hash)
        previous.merge!(new_key)
      else
        previous[new_key] = value
      end
    end
  end
end

Contoh Prependen

my_feelings_about_icecreams = {
  vanilla: 'Delicious',
  chocolate: 'Too Chocolatey',
  strawberry: 'It Is Alright...'
}

my_feelings_about_icecreams.rekey { |key| "#{key}_icecream".to_sym }
# => {:vanilla_icecream=>"Delicious", :chocolate_icecream=>"Too Chocolatey", :strawberry_icecream=>"It Is Alright..."}

Trim Contoh

{ _id: 1, ___something_: 'what?!' }.rekey do |key|
  trimmed = key.to_s.tr('_', '')
  trimmed.to_sym
end
# => {:id=>1, :something=>"what?!"}

Meratakan dan Menambahkan "Cakupan"

Jika Anda melewatkan hash kembali ke rekey itu akan menggabungkan hash yang memungkinkan Anda untuk meratakan koleksi. Ini memungkinkan kami untuk menambah cakupan kunci kami saat meratakan hash untuk menghindari menimpa kunci saat penggabungan.

people = {
  bob: {
    name: 'Bob',
    toys: [
      { what: 'car', color: 'red' },
      { what: 'ball', color: 'blue' }
    ]
  },
  tom: {
    name: 'Tom',
    toys: [
      { what: 'house', color: 'blue; da ba dee da ba die' },
      { what: 'nerf gun', color: 'metallic' }
    ]
  }
}

people.rekey do |person, person_info|
  person_info.rekey do |key|
    "#{person}_#{key}".to_sym
  end
end

# =>
# {
#   :bob_name=>"Bob",
#   :bob_toys=>[
#     {:what=>"car", :color=>"red"},
#     {:what=>"ball", :color=>"blue"}
#   ],
#   :tom_name=>"Tom",
#   :tom_toys=>[
#     {:what=>"house", :color=>"blue; da ba dee da ba die"},
#     {:what=>"nerf gun", :color=>"metallic"}
#   ]
# }

0

Jawaban sebelumnya cukup baik, tetapi mereka mungkin memperbarui data asli. Jika Anda tidak ingin data asli terpengaruh, Anda dapat mencoba kode saya.

 newhash=hash.reject{|k| k=='_id'}.merge({id:hash['_id']})

Pertama itu akan mengabaikan kunci '_id' kemudian bergabung dengan yang diperbarui.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.