ACHTUNG. Das ist ein Archiv des alten forum.ruby-portal.de. Die aktuelle Mailingliste gibt es auf lists.ruby-lang.org/pipermail/ruby-de.

NOTICE. This is a ready-only copy of the old forum.ruby-portal.de. You can find the current mailing list at lists.ruby-lang.org/pipermail/ruby-de.

Die Programmiersprache Ruby

Blog|

Forum|

Wiki  


Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]

Ein neues Thema erstellen Auf das Thema antworten  [ 19 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
BeitragVerfasst: 13 Sep 2010, 10:51 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Hallo Ruby-Freunde,

habe folgende Bedingung:



1
2
3
4
5
6
if age_ranges.size > 0
pf[:ages] = Array.new
0.upto(age_ranges.size - 1) do |x|
pf[:ages][x] = Hash[:id => x + 1,:name => DateUtil.weeks_to_string( age_ranges[x], current_locale)]
end
end


Die DateUtil.weeks_to_string gibt mit so etwas wie 3 Wochen oder 5 Monate zurück. In dem Array "age_ranges" sind die entsprechende Werte zur Berechnung der Zeiten enthalten. Allerdings kann es passieren, dass in age_ranges Bereiche drin sind wo mir "weeks_to_string" auch doppelte Einträge zurückgeben kann. Wie kann ich hierbei vermeiden, dass schon vorhandene Werte in :name doppelt vorkommen?

Vielen Dank im Voraus

Picard


PS (Moderator WoNáDo): Habe der Lesbarkeit wegen den Code entsprechend markiert.


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 14:24 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Ich muss leider zugeben, dass ich das nicht so ganz verstehe. Soweit ich das sehe erzeugts Du in jedem Array-Element (pf[:ages][x]) einen neuen Hash. Jeder dieser Hashes hat zwei Keys, zum einen :id, zum anderen :name. Mehr kommt in den jeweiligen Hash nicht hinein.

Was meinst Du jetzt mit doppelten Einträgen?

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 14:34 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Hi,

das soweit richtig.
Wenn ich pf[:ages] z.B. per puts ausgebe, stehen z.B. solche Werte drin:

name 2 Wochen id 2
name 2 Wochen id 3
name 3 Monate id 4

Es soll hierbei verhindert werden, dass z.B. 2 Wochen doppelt vorkommt. Der Wertebereich aus dem Array age_ranges erlaubt mir nicht, vorab doppelte zu erkennen, da die Inhalte über die Methode weeks_to_string aufwendig in die entsprechende Zeitangaben berechnet werden.

Besteht die Möglichkeit in der folgenden Anweisung den Rückgabewert von DateUtil.weeks_to_string abzugreifen um evtl. zu prüfen, ob ein Wert in ":name" schon exisitert?



pf[:ages][x] = Hash[:id => x + 1,:name => DateUtil.weeks_to_string( age_ranges[x], current_locale)]


Vielen Dank und beste Grüße

Picard


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 14:45 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Mal grundsätzlich - gibt es irgendeinen Grund, dass Du eine derart komplizierte Struktur zusammenbaust?

Dein Array pf enthält als Elemente Arrays, die jeweils einen Hash mit genau zwei Keys enthalten.

Was willst Du denn machen, wenn ein :name-Key einen Wert erhalten würde, der in einem anderen Hash des gleichen Arrays in pf schon vorkommt?

PS: Meine Frage etwas erweiternd - Wenn der gleiche :name-Wert für mehrere :id-Werte berechnet werden würde, hätte er dann für all diese Werte Gültigkeit oder würde nur ein :id-Wert relevant sein und die anderen damit nicht auftauchen?

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 15:02 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Hehe, die Frage ist berechtigt. Die Struktur ist nicht von mir.

Ich möchte einfach nur folgendes:
Statt...
name 2 Wochen id 2
name 2 Wochen id 3
name 3 Monate id 4

soll ein fach...
name 2 Wochen id 2
name 3 Monate id 3

... in dem Array pf stehen. D.h. Jede Zeitangabe darf nur ein mal vorkommen.

BG, Picard


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 16:58 
Offline
Interpreter
Benutzeravatar

Registriert: 21 Mai 2007, 11:30
Beiträge: 1283
Wohnort: Thüringen
War das jetzt ein Schreibfehler, dass id 3 unten 3 Monate und oben nur 2 Wochen hat?

Versteh ich dich richtig, dass du alle (ID, Name) Paare rauslöschen willst, für die es im Array an früherer Stelle bereits ein (ID, Name) Paar mit selbem Namen gibt?

Eine imo elegante Lösung wäre mit uniq, aber leider akzeptiert uniq in Ruby 1.8 noch keinen Block als Parameter. Deshalb müsstest du dir die Methode selbst schreiben:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module Enumerable
if RUBY_VERSION >= "1.9" then
alias uniq_with_block uniq
else
def uniq_with_block
hashset = {}
collection = []
each do |elem|
key = if block_given? then yield(elem) else elem end
unless hashset.has_key?(key)
hashset[key] = true
collection << elem
end
end
collection
end
end
end


Damit kannst du nun in einer Collection doppelte Elemente nach festgelegten Kriterien löschen.


1
2
3
4
5
unless age_ranges.empty?
pf[:ages] = Array.new(age_ranges.size) {|index|
{ :id => index+1, :name => DateUtil.weeks_to_string(age_ranges[index], current_locale) }
}.uniq_with_block() {|hash| hash[:name] }
end


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 17:20 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Vielen Dank zunächst für deinen Einsatz. Im Grunde geht es einfach nur darum, z.B. per unless doppelte Einträge in :name zu unterbinden.

-> Falls in dem Hash[:name] "2 Wochen" schon exisitert, einfach überspringen...
Es muss also eigentlich gar nicht dazu kommen, dass hinterher doppelte Zeitangaben gelöscht und die ":id"s neu organisiert werden müssen.
Oh je, ich hoffe du kannst mir folgen ;-)

Hier ein Bsp.:



pf[:ages][x] = Hash[:id => x + 1,:name => DateUtil.weeks_to_string( age_ranges[x], current_locale)] unless pf[:ages][:name].has_value? DateUtil.weeks_to_string( age_ranges[x], current_locale)


BG, Picard


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 17:47 
Offline
Interpreter
Benutzeravatar

Registriert: 21 Mai 2007, 11:30
Beiträge: 1283
Wohnort: Thüringen
Du kannst es natürlich auch so lösen. In dem Fall würdest du schreiben


1
2
name = DateUtil.weeks_to_string( age_ranges[x], current_locale)
pf[:ages][x] = {:id => x + 1,:name => name } unless pf[:ages].any? {|hash| hash[:name] == name}


Der Weg über uniq wäre halt in Ruby 1.9 die Standardvorgehensweise, zumal uniq sehr schnell ist, während im obigen Code alle Arrays durchsucht werden müssen.


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 17:59 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Hi,
vielen Dank. Dein letzter Ansatz bringt mich da schon weiter. Allerdings habe ich da wohl einen kleinen Denkfehler.
So lange kein Wert in pf[:ages] enthalten ist, kann ich auch nicht nach vorhanden Werten suchen. In diesem Fall würde pf[:ages].any?
es niemals erlauben, etwas in den Hash zu schreiben, weil es sich selbst aussschließen würde. Denke ich.

Wie auch immer. Habe erst vor 3 Monaten mit RoR angefangen und muss mich erst mal an die ungewöhnliche Syntax/Semantik tiefer reinarbeiten :)
Aber es macht mir trotzdem sehr viel Spaß.

vielen Dank noch mal und beste Grüße

Picard


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 18:11 
Offline
Interpreter
Benutzeravatar

Registriert: 21 Mai 2007, 11:30
Beiträge: 1283
Wohnort: Thüringen
any? gibt true zurück, wenn es ein Element in der Collection gibt, für welches der Block true zurückgibt. Wenn die Collection leer ist, müsste any? immer false zurück liefern. Da du nur Elemente einfügst, wenn any? false ausgibt, müsste es doch klappen.


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 21:15 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Kai hat geschrieben:
...aber leider akzeptiert uniq in Ruby 1.8 noch keinen Block als Parameter. Deshalb müsstest du dir die Methode selbst schreiben:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module Enumerable
if RUBY_VERSION >= "1.9" then
alias uniq_with_block uniq
else
def uniq_with_block
hashset = {}
collection = []
each do |elem|
key = if block_given? then yield(elem) else elem end
unless hashset.has_key?(key)
hashset[key] = true
collection << elem
end
end
collection
end
end
end

Die Methode existiert aber nicht in Enumerable, sondern in Array - in der Dokumentation ( RubyInstaller 1.9.2-p0) ist aber leider nicht beschrieben, dass ein Block bei uniq ausgewertet wird :? ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
p RUBY_VERSION

module Enumerable
if RUBY_VERSION >= "1.9" then
alias uniq_with_block uniq
else
def uniq_with_block
hashset = {}
collection = []
each do |elem|
key = if block_given? then yield(elem) else elem end
unless hashset.has_key?(key)
hashset[key] = true
collection << elem
end
end
collection
end
end
end

p [0,1,2,3,4,5,6,7,8,9].uniq_with_block{|v|v/2}
...ergibt...

1
2
3
"1.9.2"
uniq.rb:5:in `<module:Enumerable>': undefined method `uniq' for module `Enumerable' (NameError)
from uniq.rb:3:in `<main>'

Wenn ich es einfach unter 1.9.2 benutze (uniq), geht es einwandfrei.

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 22:23 
Offline
Interpreter
Benutzeravatar

Registriert: 21 Mai 2007, 11:30
Beiträge: 1283
Wohnort: Thüringen
Urks, hätte ich ausprobieren sollen. Ist auch schrecklich inkonsistent. Schließlich existieren map, collect etc. auch in Enumerable. Nur die destruktiven Varianten gehören zur Array-Klasse. Auf der anderen Seite habe ich uniq auch noch nie für etwas anderes außer Arrays eingesetzt (bei vielen Collections wie Hashs oder Ranges wäre uniq identisch mit entries oder to_a). Andererseits: warum sollte man bei Structs oder Strings nicht auch ab und zu uniq einsetzen wollen?

Wie auch immer, dann müsste die Methode eben in Array definiert werden =(


Nach oben
 Profil  
 
BeitragVerfasst: 13 Sep 2010, 22:40 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Sehr dumm finde ich in erster Linie, dass in der aktuellen Dokumentation zu uniq nichts von einer Möglichkeit mit Blöcken steht.

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
BeitragVerfasst: 14 Sep 2010, 12:02 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69
Irgendwie scheint der Wurm drin zu sein. Wenn ich es damit versuche:

1
2
name = DateUtil.weeks_to_string( age_ranges[x], current_locale)
pf[:ages][x] = {:id => x + 1,:name => name } unless pf[:ages].any? {|hash| hash[:name] == name}

Bekomme ich folgende Meldung:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]


Ich probiere einfach mal rum...

BG, Picard


Nach oben
 Profil  
 
BeitragVerfasst: 14 Sep 2010, 17:36 
Offline
Lehrling

Registriert: 21 Jan 2010, 10:57
Beiträge: 69


1
2
name = DateUtil.weeks_to_string( age_ranges[x], current_locale)
pf[:ages][x] = {:id => x + 1,:name => name } unless pf[:ages].any? {|hash| hash[:name] == name}
Wenn ich das ohne unless benutze, dann funzt es. Irgendwie springt es beim 5. oder 7. Durchlauf mit der Meldung undefined method `[]' for nil:NilClass, bezogen hier auf die zweite Zeile, raus. Habe jetzt einiges ausprobiert aber irgendwie habe ich wohl einen Knoten im Kopf.

BG, Picard


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 19 Beiträge ]  Gehe zu Seite 1, 2  Nächste

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 6 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach: