Come sostituire una chiave hash con un'altra chiave


192

Ho una condizione in cui ottengo un hash

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

e voglio questo hash come

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

PS : Non so quali sono le chiavi nell'hash, sono casuali che viene fornito con un prefisso "_" per ogni chiave e non voglio sottolineature



+1 per una domanda utile
ashisrai_

@ a5his: sono contento che sia stato di aiuto :)
Manish Das,

Risposte:


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

8
Mi ha salvato un paio di LOC, lo adoro!
nicohvi,

10
Spesso non mi piace il codice ruby ​​"intelligente" perché ci vuole del tempo per dire cosa sta realmente facendo. La tua soluzione è invece semplice e descrittiva.
Lucas,

3
Questa dovrebbe davvero essere la risposta accettata! Facile, pulito e diretto al punto!
GigaBass

1
Questa risposta è elegante ma in realtà non risponde alla domanda. Il post afferma che le chiavi che devono essere sostituite sono sconosciute ... Sappiamo solo che iniziano con un trattino basso, non sappiamo quali siano effettivamente le chiavi.
Dsel,

2
quindi questo crea una nuova coppia chiave / valore in cui si specifica la nuova chiave e si ottiene il valore da ciò che hash.delete :old_keyrestituisce e l'eliminazione utilizza la vecchia chiave. WOW, lo voglio tatuato da qualche parte MrGreen Grazie
Bart C

136

rotaie Hash ha un metodo standard per questo:

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

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

UPD: metodo ruby ​​2.5


4
È il metodo Rails, non standard. Buona risposta però.
user2422869,

1
Inoltre, questo metodo non può funzionare in modo ricorsivo con le chiavi hash.
Sergio Belevskij,

5
deep_transform_keys può essere usato per questo :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat

1
Alla fine! Questo è esattamente quello che ho cercato!
TiSer,

4
Questa è una parte standard della lingua a partire da Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson,

39

Se tutte le chiavi sono stringhe e tutte hanno il prefisso di sottolineatura, allora puoi correggere l'hash in atto con questo:

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

Il k[1, k.length - 1]bit prende tutto ktranne il primo personaggio. Se vuoi una copia, allora:

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

O

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

Puoi anche usarlo subse non ti piace la k[]notazione per l'estrazione di una sottostringa:

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 }

E, se solo alcune delle chiavi hanno il prefisso di sottolineatura:

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

Modifiche simili possono essere apportate a tutte le altre varianti sopra, ma queste due:

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

dovrebbe andare bene con i tasti che non hanno prefissi di sottolineatura senza ulteriori modifiche.


la tua risposta ha funzionato, ma dopo il rione ho trovato pochi hash come questo
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

@Manish: ho detto "Se tutte le chiavi sono stringhe e tutte hanno il prefisso di sottolineatura". Ho incluso un approccio di esempio per le tue "chiavi senza prefissi di sottolineatura" in un aggiornamento.
mu è troppo corto il

2
@Manish: "k" sta per "chiave", "v" è per "valore", "x" è per "non so come chiamarlo ma sono stato addestrato come matematico, quindi lo chiamo x".
mu è troppo corto il

14

tu puoi fare

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

Questo dovrebbe funzionare per il tuo caso!


11

Se vogliamo rinominare una chiave specifica nell'hash, possiamo farlo nel modo seguente:
Supponiamo che il mio hash sia my_hash = {'test' => 'ruby hash demo'}
Ora voglio sostituire 'test' con 'message', quindi:
my_hash['message'] = my_hash.delete('test')


In che modo la tua risposta è quindi una soluzione al mio problema? Se pensavi che ciò fosse utile, avresti potuto aggiungerlo nel commento sotto la domanda. La mia domanda non era quella di sostituire una chiave con un'altra chiave, la soluzione che hai dato è la proprietà Hash di base. nel mio caso non lo è: hash[:new_key] = has[:old_key]invece è: hash[:dynamic_key] = hash[:_dynamic_key]era una domanda chiara su regex e non una semplice sostituzione dell'hash.
Manish Das,

2
Sono arrivato a questo tramite una ricerca su Google e volevo la risposta di @ Swapnil. Grazie
toobulkeh,

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

4
Mi piace il fatto che tu abbia provato a usare un regex per filtrare correttamente i caratteri di sottolineatura, ma dovresti essere consapevole che in ruby, a differenza di javascript e altri, / ^ / significa 'inizio della stringa O LINEA' e / $ / significa 'fine di stringa OR LINE '. È improbabile che in questo caso le chiavi abbiano una nuova riga, ma dovresti essere consapevole che l'uso di questi due operatori in Ruby non è solo soggetto a errori ma è anche molto pericoloso se usato in modo errato nelle validazioni contro le iniezioni. Vedi qui per una spiegazione. Spero non ti dispiaccia diffondere la consapevolezza.
Jorn van de Beek,

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

1

Ho esagerato e ho pensato a quanto segue. La mia motivazione dietro questo era di aggiungere chiavi hash per evitare conflitti di portata quando si uniscono / appiattiscono gli hash.

Esempi

Estendi classe hash

Aggiunge il metodo rekey alle istanze 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

Esempio di anteprime

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

Esempio di ritaglio

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

Appiattimento e aggiunta di un "ambito"

Se si passa nuovamente un hash a rekey, si unirà l'hash che consente di appiattire le raccolte. Questo ci consente di aggiungere ambito alle nostre chiavi quando si appiattisce un hash per evitare di sovrascrivere una chiave dopo l'unione.

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

Le risposte precedenti sono abbastanza buone, ma potrebbero aggiornare i dati originali. Nel caso in cui non desideri che i dati originali vengano interessati, puoi provare il mio codice.

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

Innanzitutto ignorerà la chiave '_id', quindi verrà unita a quella aggiornata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.