Hash con due elementi come chiave


#1

ciao a tutti, ho il seguente hash

@strade = Hash([])

nel quale associo ad una coppia di citta i kilometri

esempio @strade[[“milano”,“roma”]] = [650, 800]
vuolre dire che ci sono due strade da milano a roma, una di 600 km e una
di 800 km.

in un controllo ho notato che il sistema non fa distinzione tra
@strade[[“milano”,“roma”]] e @strade[[“roma”,“milano”]] e ho bisogno che
siano due cose distinte, anche perchè ci possono essere ulteriori strade
e ulteriori kilometri… come posso fare?

grazie a tutti


#2

2008/12/5 Njna N. removed_email_address@domain.invalid:

in un controllo ho notato che il sistema non fa distinzione tra
@strade[[“milano”,“roma”]] e @strade[[“roma”,“milano”]] e ho bisogno che
siano due cose distinte, anche perchè ci possono essere ulteriori strade
e ulteriori kilometri… come posso fare?

ciao, non è quello che hai chiesto, ma perché non usare una hash tipo
{“Roma” => {“Milano”, …}, …}?
i dati in che forma li hai?

riguardo alla domanda, non mi viene in mente una soluzione usando
Array; che ne dici di costruire la chiave, ad esempio “Roma | Milano”?


#3

ciao Pietro, come faccio a costruire la chiave “Roma | Milano”? ho
proprio esigenza di associare ad una coppia di dati più valori che
sarebbero i chilometri

Pietro G. wrote:

2008/12/5 Njna N. removed_email_address@domain.invalid:

in un controllo ho notato che il sistema non fa distinzione tra
@strade[[“milano”,“roma”]] e @strade[[“roma”,“milano”]] e ho bisogno che
siano due cose distinte, anche perch� ci possono essere ulteriori strade
e ulteriori kilometri… come posso fare?

ciao, non � quello che hai chiesto, ma perch� non usare una hash tipo
{“Roma” => {“Milano”, …}, …}?
i dati in che forma li hai?

riguardo alla domanda, non mi viene in mente una soluzione usando
Array; che ne dici di costruire la chiave, ad esempio “Roma | Milano”?


#4

2008/12/5 Njna N. removed_email_address@domain.invalid

ciao Pietro, come faccio a costruire la chiave “Roma | Milano”? ho
proprio esigenza di associare ad una coppia di dati più valori che
sarebbero i chilometri

Non so se sia quello che serve a te, ma riprendendo quanto detto da njna
ti
faccio un semplice esempio utilizzando le Hash.

irb(main):001:0> strade = {} #=> {}
irb(main):002:0> strade[‘milano’] = {‘roma’ => [650, 800]} #=>
{“roma”=>[650, 800]}
irb(main):003:0> strade #=> {“milano”=>{“roma”=>[650, 800]}}
irb(main):004:0> strade[‘roma’] = {‘milano’ => [800, 650]} #=>
{“milano”=>[800, 650]}
irb(main):005:0> strade[‘milano’][‘roma’] #=> [650, 800]
irb(main):006:0> strade[‘roma’][‘milano’] #=> [800, 650]

Come vedi dalle due ultime righe se metti in ordine differente le
città che
ti interessano avrai dei risultati differenti.


Andrea R., http://mikamai.com
Writing http://sensejs.wordpress.com/
Collaborating http://therubymine.it
Reading http://stacktrace.it


#5

Il 5 dicembre 2008 15.28, Njna N. removed_email_address@domain.invalid ha scritto:

quindi dichiaro il mio hash così:

@strade = Hash.new{}

anche @strade = {} va bene

poi quando aggiungo la prima coppia supponiamo milano - roma km 600
faccio @strade[milano] = ??? e poi?

@strade = {}

def get_route(partenza, destinazione)
@strade[partenza] && @strade[partenza][destinazione]
end

def set_route(partenza, destinazione, altro)
@strade[partenza] ||= {}
@strade[partenza][destinazione] = altro
end


#6

On Fri, 5 Dec 2008 14:59:36 +0100, Njna N. wrote:

ciao Pietro, come faccio a costruire la chiave “Roma | Milano”? ho
proprio esigenza di associare ad una coppia di dati più valori che
sarebbero i chilometri

Perchè non creare invece un oggetto Itinerario, con due attributi
(@partenza e @arrivo, ad esempio)?
A quel punto useresti un’istanza di quell’oggetto come chiave e saresti
libero di definire le condizioni di identità di due oggetti.
E soprattutto avresti codice molto più pulito e object oriented.

Andrea


#7

può aver senso questo? faccio inserimento controllano se c’era già qlc
valore o meno

def aggiungi_strada(c1,c2,t)
if (@strade[c1][c2]!=nil)
@strade[c1][c2] << t
else
@strade[c1]= {}
@strade[c1][c2] = t
end
end

Pietro G. wrote:

Il 5 dicembre 2008 15.28, Njna N. removed_email_address@domain.invalid ha scritto:

quindi dichiaro il mio hash cos�:

@strade = Hash.new{}

anche @strade = {} va bene

poi quando aggiungo la prima coppia supponiamo milano - roma km 600
faccio @strade[milano] = ??? e poi?

@strade = {}

def get_route(partenza, destinazione)
@strade[partenza] && @strade[partenza][destinazione]
end

def set_route(partenza, destinazione, altro)
@strade[partenza] ||= {}
@strade[partenza][destinazione] = altro
end


#8

quindi dichiaro il mio hash così:

@strade = Hash.new{}

poi quando aggiungo la prima coppia supponiamo milano - roma km 600
faccio @strade[milano] = ??? e poi?

Andrea R. wrote:

2008/12/5 Njna N. removed_email_address@domain.invalid

ciao Pietro, come faccio a costruire la chiave “Roma | Milano”? ho
proprio esigenza di associare ad una coppia di dati pi� valori che
sarebbero i chilometri

Non so se sia quello che serve a te, ma riprendendo quanto detto da njna
ti
faccio un semplice esempio utilizzando le Hash.

irb(main):001:0> strade = {} #=> {}
irb(main):002:0> strade[‘milano’] = {‘roma’ => [650, 800]} #=>
{“roma”=>[650, 800]}
irb(main):003:0> strade #=> {“milano”=>{“roma”=>[650, 800]}}
irb(main):004:0> strade[‘roma’] = {‘milano’ => [800, 650]} #=>
{“milano”=>[800, 650]}
irb(main):005:0> strade[‘milano’][‘roma’] #=> [650, 800]
irb(main):006:0> strade[‘roma’][‘milano’] #=> [800, 650]

Come vedi dalle due ultime righe se metti in ordine differente le
citt� che
ti interessano avrai dei risultati differenti.


Andrea R., http://mikamai.com
Writing http://sensejs.wordpress.com/
Collaborating http://therubymine.it
Reading http://stacktrace.it


#9

andrea riesci a farmi un esempio di come faresti tu? così mi schiarisco
le idee che ho un sacco di confusione
grazie mille

Andrea C. wrote:

On Fri, 5 Dec 2008 14:59:36 +0100, Njna N. wrote:

ciao Pietro, come faccio a costruire la chiave “Roma | Milano”? ho
proprio esigenza di associare ad una coppia di dati pi� valori che
sarebbero i chilometri

Perch� non creare invece un oggetto Itinerario, con due attributi
(@partenza e @arrivo, ad esempio)?
A quel punto useresti un’istanza di quell’oggetto come chiave e saresti
libero di definire le condizioni di identit� di due oggetti.
E soprattutto avresti codice molto pi� pulito e object oriented.

Andrea


#10

Perfetto, funziona!!! grazie a tutti!!!
solo più una domanda… perchè non sempre è necessario definire l’hash
in quel modo?

uso spesso l’hash ma non mi era mai capitata l’esigenza di una
inizializzazione del genere… come mai?

ancora grazie!!

Pierpaolo S. wrote:

Ciao,
secondo me il problema sta nel modo in cui inizializzi l’hash
@strade = Hash([])
cos� assegni lo stesso array per tuttu le chiavi dell’hash.Per assegnare
un
array diverso per ogni chiave devi fare cos�
@strade = Hash.new {|h,k| h[k] = []}

ciao
2008/12/5 Njna N. removed_email_address@domain.invalid


#11

Ciao,
secondo me il problema sta nel modo in cui inizializzi l’hash
@strade = Hash([])
così assegni lo stesso array per tuttu le chiavi dell’hash.Per assegnare
un
array diverso per ogni chiave devi fare così
@strade = Hash.new {|h,k| h[k] = []}

ciao
2008/12/5 Njna N. removed_email_address@domain.invalid


#12

ok ma quando basta mettere Hash{} e quando invece è necessario
specificare tra parentesi una cosa del genere? {|h,k| h[k] = []}


#13

2008/12/5 Njna N. removed_email_address@domain.invalid

Perfetto, funziona!!! grazie a tutti!!!
solo più una domanda… perchè non sempre è necessario definire l’hash
in quel modo?

uso spesso l’hash ma non mi era mai capitata l’esigenza di una
inizializzazione del genere… come mai?

Allora, per definire una Hash base i comandi che si possono usare sono
due
(una vale l’altra)

strade = Hash.new
strade = {}

A dirla tutta Hash([]) non l’ho mai visto utilizzare, e almeno a mio
parere
non è molto chiaro.
Comunque l’importante è che abbia rislto il prob :wink:


Andrea R., http://mikamai.com
Writing http://sensejs.wordpress.com/
Collaborating http://therubymine.it
Reading http://stacktrace.it


#14

On Fri, Dec 5, 2008 at 5:05 PM, Njna N. removed_email_address@domain.invalid
wrote:

ok ma quando basta mettere Hash{} e quando invece è necessario
specificare tra parentesi una cosa del genere? {|h,k| h[k] = []}

Credo si stia fecendo un pochino di confusione.
Guardando la documentazione ufficiale sulla classe
Hashhttp://www.ruby-doc.org/core/classes/Hash.htmlse specifichi una
cosa del genere significa che per ogni chiave non
specificata avrai un array vuoto, cioè []. Ecco un esempio

irb(main):012:0> strade = Hash.new([])
=> {}
irb(main):013:0> strade[‘milano’] = {‘roma’ => 650}
=> {“roma”=>650}
irb(main):014:0> strade[‘milano’]
=> {“roma”=>650}
irb(main):015:0> strade[‘bologna’]
=> []

Come vedi, per la chiave Bologna non è ancora stata definita una chiave,
quindi mi ritorna il default che è stato definito come array vuoto, cioè [].
La seconda notazione che chiedi è un pochino più potente, ma in questo caso
non cambia nulla.

irb(main):016:0> strade = Hash.new{|h, k| h[k] = []}
=> {}
irb(main):017:0> strade[‘milano’] = {‘roma’ => 650}
=> {“roma”=>650}
irb(main):018:0> strade[‘milano’]
=> {“roma”=>650}
irb(main):019:0> strade[‘bologna’]
=> []

Se sei curioso troverai altro nella doc ufficiale per qualche caso più
complesso :wink:


Andrea R., http://mikamai.com
Writing http://sensejs.wordpress.com/
Collaborating http://therubymine.it
Reading http://stacktrace.it


#15

On Fri, 5 Dec 2008 16:00:57 +0100, Njna N. wrote:

può aver senso questo? faccio inserimento controllano se c’era già qlc
valore o meno

def aggiungi_strada(c1,c2,t)
if (@strade[c1][c2]!=nil)

Al limite, @strade[c1].has_key?(c2)

       @strade[c1][c2] << t
  else
       @strade[c1]= {}
       @strade[c1][c2] = t
  end

end

A meno che t sia un array, non può funzionare, visto che nel primo caso
appendi e nel secondo assegni.

Onestamente trovo questo approccio eccessivamente procedurale. Con
rispetto parlando, Ruby non è PHP.

Ciao,
Andrea


#16

On Fri, 5 Dec 2008 16:03:14 +0100, Njna N. wrote:

andrea riesci a farmi un esempio di come faresti tu? così mi schiarisco
le idee che ho un sacco di confusione
grazie mille

class Itinerario
def initialize(args = {})
@a = args
end

def from
@a[:from]
end

def to
@a[:to]
end

def eql?(other)
return self.from == other.from && self.to == other.to
end

def hash
return (self.from.hash << 16) | self.to.hash
end
end

Facendo così puoi scrivere:

strade = {}
strade[Itinerario.new(:from => ‘Milano’, :to => ‘Roma’)] = 650

o anche

s = {
Itinerario.new(:from => ‘Milano’, :to => ‘Roma’) = 650 # sovrascrive
quello precedente
Itinerario.new({:from => ‘Milano’, :to => ‘Brescia’}) => 50,
Itinerario.new({:from => ‘Roma’, :to => ‘Milano’}) => 700
}
strade.merge!(s)

puts strade.inspect

La cosa più comoda è che se un domani vuoi supportare anche il caso di
Milano -> Roma via Ancona, cambi solo la classe Itinerario e hai fatto.

Ciao,
Andrea


#17

Prima ho espresso un pò male il concetto. Con

Hash.new(obj)

viene ustao lo stesso oggetto (obj) per tutti i valori di default.
Invece
con

Hash.new {|h,k| …}

ogni volta che richiedi il valore di una chiave non ancora presente
nell’hash viene chiamato il blocco, che dovrebbe restituire il valore di
default, ma in questo caso è un oggetto differente di volta in volta.
io utilizzo questa forma

h = Hash.new {|h,k| h[k] = []}

perchè così non devo preoccuparmi di fare i controlli sulla chiave prima di
appendere un elemento nell’array , cioè posso semplicamente fare così

h[“nouva chiave”] << “elemento da appendere”

in questo modo se la chiave è già presente, il nuovo elemento viene accodato
all’array, se la chiave non è presente viene creato un nuovo array e poi
inserito l’elemento
altrimenti dovrei fare così

if h.has_key?(“nouva chiave”)
h << “elemento da appendere”
else
h["nuova chiave] = []
h["nuova chiave] << “elemento da appendere”
end

2008/12/5 Njna N. removed_email_address@domain.invalid


#18

con

Hash.new {|h,k| …}

ogni volta che richiedi il valore di una chiave non ancora presente
nell’hash viene chiamato il blocco, che dovrebbe restituire il valore di
default, ma in questo caso è un oggetto differente di volta in volta.

Aggiungo: probabilmente non ti sei mai scontrato con il problema
perché usavi come valore predefinito un oggetto immutabile, ad esempio un
numero.

Per cui facendo
hsh = Hash.new(0)
hsh[:foo] +=1
puts hsh[:foo], hsh[:bar] #=> 1 0

In questo caso non stavi modificando l’oggetto “0” ma lo stavi
sostituendo con l’oggetto “1”, mentre nel caso degli array il
cambiamento avveniva nell’oggetto immagazzinato come valore.


#19

Magari complico solo la situazione, ma perche non usi un grafo per
rappresentare le strade tra le citta’?

Nello specifico dovrebbe essere un grafo direzionato con le citta’
come nodi (vertici) e le distanze in kilometri sono attributi degli
edges

Ho trovato questa libreria in Ruby:
http://rgl.rubyforge.org/rgl/index.html

tipo:

require ‘rgl/adjacency’
dg=RGL::DirectedAdjacencyGraph[“Milano”,“Roma”,“Roma”,“Milano”]
=> #<RGL::DirectedAdjacencyGraph:0x5aa500
@vertice_dict={“Roma”=>#<Set: {“Milano”}>, “Milano”=>#<Set:
{“Roma”}>}, @edgelist_class=Set>

dg.vertices
=> [“Roma”, “Milano”]

dg.edges
=> [#<RGL::Edge::DirectedEdge:0x5a5a78 @target=“Milano”,
@source=“Roma”>, #<RGL::Edge::DirectedEdge:0x5a5910 @target=“Roma”,
@source=“Milano”>]

Siccome il grafo e’ direzionato abbiamo entrambi gli edges (quindi
distingue tra roma–>milano e milano–>roma).

A questo punto non ti rimane che attaccare agli edges un attributo (la
distanza in kilometri)

La cosa interessante e’ che, essendo un grafo, ci puoi applicare una
serie di interessanti metodi di ricerca, sorting, shortest paths, etc…

ciao

Il giorno 05/dic/08, alle ore 14:36, Njna N. ha scritto: