วิธีการแทนที่คีย์แฮชด้วยคีย์อื่น


192

ฉันมีเงื่อนไขที่ฉันได้รับแฮช

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

และฉันต้องการแฮชนี้เป็น

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

PS : ฉันไม่รู้ว่าอะไรคือกุญแจในแฮชพวกมันสุ่มซึ่งมาพร้อมกับคำนำหน้า "_" สำหรับทุกคีย์และฉันไม่ต้องการขีดเส้นใต้


สิ่งนี้อาจช่วยคุณได้: stackoverflow.com/questions/4044451/…
corroded

+1 สำหรับคำถามที่มีประโยชน์
ashisrai_

@ a5his: ฉันดีใจที่มันเป็นความช่วยเหลือ :)
Manish Das

คำตอบ:


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

8
บันทึก LOC ให้ฉันสองสามคนรักมัน!
nicohvi

10
ฉันไม่ชอบรหัสทับทิม "ฉลาด" เพราะใช้เวลาพอสมควรในการบอกว่ามันทำอะไรอยู่ โซลูชันของคุณนั้นเรียบง่ายและมีความหมาย
ลูคัส

3
นี่ควรเป็นคำตอบที่ได้รับการยอมรับอย่างแน่นอน! ง่ายสะอาดและตรงประเด็น!
GigaBass

1
คำตอบนี้งดงาม แต่จริงๆแล้วมันไม่ได้ตอบคำถาม โพสต์ระบุว่ากุญแจที่ต้องการเปลี่ยนนั้นไม่เป็นที่รู้จัก ... เรารู้เพียงว่าพวกเขาเริ่มต้นด้วยการขีดเส้นใต้เราไม่รู้ว่ากุญแจคืออะไร
เดล

2
ดังนั้นสิ่งนี้จะสร้างคู่คีย์ / ค่าใหม่ที่คุณระบุคีย์ใหม่และรับค่าจากสิ่งที่hash.delete :old_keyส่งคืนและการลบใช้คีย์เก่า ว้าวฉันต้องการมันสักที่ไหนสักแห่ง :-D ขอบคุณ
Bart C

136

rails Hash มีวิธีการมาตรฐานสำหรับมัน:

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

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

UPD: วิธี ruby ​​2.5


4
เป็นวิธี Rails ไม่ใช่แบบมาตรฐาน คำตอบที่ดีแม้ว่า
2422869

1
นอกจากนี้วิธีนี้ไม่สามารถใช้งานได้กับแป้นแฮชซ้ำ ๆ
Sergio Belevskij

5
deep_transform_keys สามารถใช้ได้ :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat

1
ในที่สุด! นี่คือสิ่งที่ฉันค้นหา!
TiSer

4
นี่เป็นส่วนมาตรฐานของภาษาตั้งแต่ Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson

39

หากคีย์ทั้งหมดเป็นสตริงและทุกปุ่มมีคำนำหน้าขีดล่างคุณสามารถแก้ไขแฮชได้ด้วยวิธีนี้

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

k[1, k.length - 1]บิตคว้าทั้งหมดkยกเว้นตัวอักษรตัวแรก หากคุณต้องการสำเนาจากนั้น:

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

หรือ

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

คุณสามารถใช้subถ้าคุณไม่ชอบk[]สัญกรณ์สำหรับการแยกสตริงย่อย:

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 }

และถ้ามีเพียงบางส่วนของคีย์ที่มีคำนำหน้าขีดล่าง:

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

การปรับเปลี่ยนที่คล้ายกันสามารถทำได้กับตัวแปรอื่นทั้งหมดด้านบน แต่ทั้งสองอย่างนี้:

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

ควรใช้กับคีย์ที่ไม่มีคำนำหน้าขีดล่างโดยไม่มีการแก้ไขเพิ่มเติม


คำตอบของคุณทำงานได้ แต่หลังจากวอร์ดฉันพบแฮชบางอย่างเช่นนี้
Manish Das

3
{"_id" => "4de7140772f8be03da000018", "_type" => "เวิร์กสเตชัน", "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" => "เวิร์กสเตชัน", "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

@ หายไป: ฉันพูดว่า "ถ้าคีย์ทั้งหมดเป็นสตริงและทั้งหมดนั้นมีคำนำหน้าขีดเส้นใต้" ฉันได้รวมวิธีการตัวอย่างสำหรับ "คีย์ของคุณโดยไม่มีเครื่องหมายขีดล่าง" ในการอัปเดต
mu สั้นเกินไป

2
@Manish: "k" สำหรับ "สำคัญ", "v" สำหรับ "ค่า", "x" สำหรับ "ฉันไม่รู้ว่าจะเรียกมันว่าอะไร แต่ฉันได้รับการฝึกฝนให้เป็นนักคณิตศาสตร์ดังนั้นฉันจึงเรียกมันว่า x"
mu สั้นเกินไป


11

หากเราต้องการเปลี่ยนชื่อคีย์เฉพาะในแฮชเราสามารถทำได้ดังนี้:
สมมติว่าแฮชของฉันคือ my_hash = {'test' => 'ruby hash demo'}
ตอนนี้ฉันต้องการแทนที่ 'ทดสอบ' โดย 'ข้อความ' แล้ว:
my_hash['message'] = my_hash.delete('test')


คำตอบของคุณเป็นวิธีแก้ปัญหาของฉันได้อย่างไร หากคุณคิดว่าสิ่งนี้มีประโยชน์คุณก็สามารถเพิ่มความคิดเห็นภายใต้คำถามได้ คำถามของฉันไม่ได้แทนที่คีย์ด้วยคีย์อื่นโซลูชันที่คุณมอบให้เป็นคุณสมบัติแฮชพื้นฐานมาก ในกรณีของฉันมันไม่ใช่: hash[:new_key] = has[:old_key]แต่hash[:dynamic_key] = hash[:_dynamic_key]มันเป็นคำถามที่ชัดเจนเกี่ยวกับ regex และไม่ใช่แฮชแบบง่าย
Manish Das

2
ฉันมาที่นี่ผ่านการค้นหา google และต้องการคำตอบของ @ Swapnil ขอบคุณ
toobulkeh

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

4
ฉันชอบที่คุณพยายามใช้ regex เพื่อกรองเครื่องหมายขีดล่างอย่างถูกต้อง แต่คุณควรระวังว่าใน ruby ​​ซึ่งแตกต่างจาก javascript และอื่น ๆ / ^ / หมายถึง 'เริ่มต้นสตริงหรือ LINE' และ / $ / หมายถึง 'สิ้นสุดของ สตริงหรือ LINE ' มันไม่น่าเป็นไปได้ที่คีย์จะมีการขึ้นบรรทัดใหม่ในกรณีนี้ แต่คุณควรระวังว่าการใช้ตัวดำเนินการทั้งสองในทับทิมไม่เพียง แต่เกิดข้อผิดพลาดได้ง่าย แต่ยังเป็นอันตรายมากเมื่อใช้ผิดในการตรวจสอบการฉีด ดูที่นี่สำหรับคำอธิบาย หวังว่าคุณจะไม่รังเกียจที่จะเผยแพร่ความรู้
Jorn van de Beek


1

ฉันไปมากเกินไปและเกิดขึ้นกับสิ่งต่อไปนี้ แรงจูงใจของฉันที่อยู่เบื้องหลังสิ่งนี้คือการผนวกคีย์แฮชเพื่อหลีกเลี่ยงความขัดแย้งของขอบเขตเมื่อรวมเข้าด้วยกัน / แฮชที่แบนราบ

ตัวอย่าง

ขยายคลาสแฮช

เพิ่มเมธอด rekey ให้กับอินสแตนซ์ 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

ตัวอย่างการเตรียม

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..."}

ตัดตัวอย่าง

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

แบนและผนวก "ขอบเขต"

หากคุณผ่านแฮชกลับไปที่ rekey มันจะรวมแฮที่อนุญาตให้คุณแผ่คอลเลกชัน สิ่งนี้ช่วยให้เราสามารถเพิ่มขอบเขตให้กับคีย์ของเราเมื่อทำการแฮชที่แบนราบเพื่อหลีกเลี่ยงการเขียนทับคีย์เมื่อทำการผสาน

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

คำตอบก่อนหน้านี้ดีพอ แต่อาจอัพเดตข้อมูลดั้งเดิม ในกรณีที่คุณไม่ต้องการให้ข้อมูลต้นฉบับได้รับผลกระทบคุณสามารถลองใช้รหัสของฉันได้

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

ก่อนอื่นมันจะเพิกเฉยต่อคีย์ '_id' จากนั้นรวมกับคีย์ที่อัพเดต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.