Merging hashes and having trouble with variable scope!

A

Andy Pipes

Hi.

Caveat: I'm just learning Ruby coming from some basic PHP programming.

In the following I am aiming to peer into two tab-separated files with
some vegetable facts on them. In the first 'column' of each file there
are vegetable names, followed by the data. However, whilst some of the
vegetables have the same name, others do not. My ultimate aim is to take
the contents of each of these files, and merge them into a master file,
and for those vegetables that have data in both files to merge the data.

So a line from file 1 might read:

Watermelon 60-80 4-10

and one file two:

Watermelon 1/2oz. 34 12-26 24-48

and in the masterfile I would want:

Watermelon 60-80 4-10 1/2oz. 34 12-26 24-48.

I have been trying unsuccessfully for the past two days to do this so
any help is greatly appreciated!!! :)

Here's some sample code:


File.foreach("vegetables.txt") do |line|
name, temp, germ_days = line.split /\t/
#split min and max germination temps
if temp =~ /-/
temp_min, temp_max = temp.split /-/
else temp_min = temp and temp_max = temp
end
# split the min and max germ days
if germ_days =~ /-/
germ_days_min, germ_days_max = germ_days.split /-/
else germ_days_min = germ_days and germ_days_max = germ_days
end

# now iterate over the other table
File.foreach("vegetables2.txt") do |l|
name2, crop_yield, space_between_rows, space_between_plants,
days_to_harvest = l.split /\t/
#split min and max space between rows
if space_between_rows =~ /-/
space_between_rows_min, space_between_rows_max =
space_between_rows.split /-/
else
space_between_rows_min = space_between_rows and
space_between_rows_max = space_between_rows
end
# split the min and max space between plants
if space_between_plants =~ /-/
space_between_plants_min, space_between_plants_max =
space_between_plants.split /-/
else
space_between_plants_min = space_between_plants and
space_between_plants_max = space_between_plants
end
# split the min and max days to harvest
if days_to_harvest =~ /-/
days_to_harvest_min, days_to_harvest_max = days_to_harvest.split
/-/
else
days_to_harvest_min = days_to_harvest and days_to_harvest_max =
days_to_harvest
end
if name2 == name
veggies = {
:name => name2,
:crop_yield => crop_yield,
:temp_min => temp_min,
:temp_max => temp_max,
:germ_days_min => germ_days_min,
:germ_days_max => germ_days_max,
:space_between_plants_min => space_between_plants_min,
:space_between_plants_max => space_between_plants_max,
:space_between_rows_min => space_between_rows_min,
:space_between_rows_max => space_between_rows_min,
:days_to_harvest_min => days_to_harvest_min,
:days_to_harvest_max => days_to_harvest_max
}
end

puts veggies

end
end

If I run this, I get local variable not found errors; I've tried a lot
of variations, with no luck. I'm new to OO, and think that maybe I'm
just 'not getting it' in this case.

Can anyone help? Thanks in advance.

Andy
 
A

Andy Pipes

Sorry: worth mentioning - I'm just trying to see what the output of the
'veggies' hash is here, not writing it to a separate file yet..
 
D

David A. Black

Hi --

Hi.

Caveat: I'm just learning Ruby coming from some basic PHP programming.

In the following I am aiming to peer into two tab-separated files with
some vegetable facts on them. In the first 'column' of each file there
are vegetable names, followed by the data. However, whilst some of the
vegetables have the same name, others do not. My ultimate aim is to take
the contents of each of these files, and merge them into a master file,
and for those vegetables that have data in both files to merge the data.

So a line from file 1 might read:

Watermelon 60-80 4-10

and one file two:

Watermelon 1/2oz. 34 12-26 24-48

and in the masterfile I would want:

Watermelon 60-80 4-10 1/2oz. 34 12-26 24-48.

I have been trying unsuccessfully for the past two days to do this so
any help is greatly appreciated!!! :)

Here's some sample code:

Back at ya :) Try this. I've changed key names just to fit things on
single lines and make my code look even slicker than it is :) You can
change all that back, of course. The main thing is to absolutely not
write that same splitting routine five times! I've syphoned it off
into a method (and you could possibly even trim the arguments down if
you automated the "min"/"max" parts).

So, see if this helps as a starting point. It doesn't distinguish
among different vegetables, but that could be added.

@v = {}

def split_or_double(string, key1, key2)
@v[key1], @v[key2] = string.split(/-/)
@v[key2] || @v[key1]
end

File.foreach("vegetables.txt") do |line|
name, temp, germ_days = line.split
split_or_double(temp, :temp_max, :temp_min)
split_or_double(germ_days, :germ_days_max, :germ_days_min)
end

File.foreach("vegetables2.txt") do |line|
name2, crop_yield, row_space, plant_space, harvest_days = line.split
split_or_double(row_space, :min_row_space, :max_row_space)
split_or_double(plant_space, :min_plant_space, :max_plant_space)
split_or_double(harvest_days, :min_harvest, :max_harvest)
end

p @v


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!
 
T

Todd Benson

Hi.

Caveat: I'm just learning Ruby coming from some basic PHP programming.

In the following I am aiming to peer into two tab-separated files with
some vegetable facts on them. In the first 'column' of each file there
are vegetable names, followed by the data. However, whilst some of the
vegetables have the same name, others do not. My ultimate aim is to take
the contents of each of these files, and merge them into a master file,
and for those vegetables that have data in both files to merge the data.

So a line from file 1 might read:

Watermelon 60-80 4-10

and one file two:

Watermelon 1/2oz. 34 12-26 24-48

and in the masterfile I would want:

Watermelon 60-80 4-10 1/2oz. 34 12-26 24-48.

I have been trying unsuccessfully for the past two days to do this so
any help is greatly appreciated!!! :)

Here's some sample code:


File.foreach("vegetables.txt") do |line|
name, temp, germ_days = line.split /\t/
#split min and max germination temps
if temp =~ /-/
temp_min, temp_max = temp.split /-/
else temp_min = temp and temp_max = temp
end
# split the min and max germ days
if germ_days =~ /-/
germ_days_min, germ_days_max = germ_days.split /-/
else germ_days_min = germ_days and germ_days_max = germ_days
end

# now iterate over the other table
File.foreach("vegetables2.txt") do |l|
name2, crop_yield, space_between_rows, space_between_plants,
days_to_harvest = l.split /\t/
#split min and max space between rows
if space_between_rows =~ /-/
space_between_rows_min, space_between_rows_max =
space_between_rows.split /-/
else
space_between_rows_min = space_between_rows and
space_between_rows_max = space_between_rows
end
# split the min and max space between plants
if space_between_plants =~ /-/
space_between_plants_min, space_between_plants_max =
space_between_plants.split /-/
else
space_between_plants_min = space_between_plants and
space_between_plants_max = space_between_plants
end
# split the min and max days to harvest
if days_to_harvest =~ /-/
days_to_harvest_min, days_to_harvest_max = days_to_harvest.split
/-/
else
days_to_harvest_min = days_to_harvest and days_to_harvest_max =
days_to_harvest
end
if name2 == name
veggies = {
:name => name2,
:crop_yield => crop_yield,
:temp_min => temp_min,
:temp_max => temp_max,
:germ_days_min => germ_days_min,
:germ_days_max => germ_days_max,
:space_between_plants_min => space_between_plants_min,
:space_between_plants_max => space_between_plants_max,
:space_between_rows_min => space_between_rows_min,
:space_between_rows_max => space_between_rows_min,
:days_to_harvest_min => days_to_harvest_min,
:days_to_harvest_max => days_to_harvest_max
}
end

puts veggies

end
end

If I run this, I get local variable not found errors; I've tried a lot
of variations, with no luck. I'm new to OO, and think that maybe I'm
just 'not getting it' in this case.

Can anyone help? Thanks in advance.

Andy

For scope, you must establish for the interpreter that these variables
exist outside of the File.foreach blocks.

my_var = ""
File.foreach {|line| #do_something_with_my_var}


Personally, I might try using ranges (needs cleaning up)...

#add method to range for building from
# a string like "1-10"
class Range
def self.[](str)
begin
a = str.split(/-/)
a[1] ||= [0]
new(*(a.map {|i| Integer(i)})) rescue str
end
end
end

#just a method to split and change to ranges
def collate_veggie str
str.chomp.split(/\t/).map! {|elem| Range[elem]}
end

#first set of data
veggies1 = {}
keys1 = :temp, :germ_days
File.foreach('veggies1.txt' ) do |line|
values = collate_veggie line
veggies1[values.shift] = keys1.zip values
end

#second set of data
veggies2 = {}
keys2 = :crop_yield, :row_space, :plant_space, :harvest_days
File.foreach('veggies2.txt') do |line|
values = collate_veggie line
veggies2[values.shift] = keys2.zip values
end

#merge the two
veggies = {}
veggies1.each_key {|k| veggies[k] = Hash[*(veggies1[k] | veggies2[k]).flatten]}

#use Range methods for min and max
p h
p h["Watermelon"][:germ_days].min
p h["JuicyVeggie"][:plant_space].max


just a thought,
Todd
 
T

Todd Benson

#use Range methods for min and max
p h
p h["Watermelon"][:germ_days].min
p h["JuicyVeggie"][:plant_space].max

The h's should be veggies', so...

p veggies
p veggies["Watermelon"][:germ_days].min
p veggies["JuicyVeggie"][:plant_space].max


Oops,
Todd
 
T

Todd Benson

class Range
def self.[](str)
begin
a = str.split(/-/)
a[1] ||= [0]
new(*(a.map {|i| Integer(i)})) rescue str
end
end
end

Another typo of mine. The a[1] ||= [0] should be a[1] ||= a[0].

My apologies for these minor things, but I hand type in my answers,
because, in using Gmail, my cut and pastes sometimes get "folded"
(unseen unless "- Show quoted text -" is clicked upon).

Todd
 
A

Andy Pipes

I'd just like to say thank you to both of you. Will have a go amending
the (admittedly spaghetti) code later and see how it goes.

best, andy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,737
Latest member
Georgeengab

Latest Threads

Top