I am trying to iterate through an array in Ruby. I using eachmethod but getting the following error message: NoMethodError: undefined method ``each' for "1, 2":String.
I am using jruby-1.6.7. Not sure if that is the problem.
Here is the code:
#!/usr/bin/ruby
puts "Select one of these.\n1. ABC\n2. DEF\n3. GHI\n"
schemas = gets.chomp
len = schemas.size
puts schemas.split(",")
puts schemas[0..len]
schemas.each {|x| puts x}
I need some guidance with iterating through a simple array in Ruby?
Thanks.
You were on the right track:
schemas = gets.chomp
# => "1, 2"
# save the result of split into an array
arr = schemas.split(",")
# => [1, 2]
# loop the array
arr.each { |schema| puts schema }
# => 1
# => 2
You can also do this in one line, though the array won't get saved anywhere.
schemas = gets.chomp
schemas.split(",").each { |schema| puts schema }
# => 1
# => 2
You have the right idea, however you are calling the Array#each method on a String.
schemas = gets.chomp
puts schemas.split(",")
It's true that the String#split method converts a string to an array, however you never actually converted the data type. schemas is still recognized as a string.
What you could do is
schemas = schemas.split(",")
Then
schemas.each{|x| puts x}
Related
I have small script I'm working on:
while (true)
s = gets.chomp #String
if not s
break;
end
values = s.split
operation = values[0]
amount = values[1].to_i
puts values
puts values.class
end
This is the output:
I inputted 'W 400', and the program printed out the array as requested. However, why is the array not outputted in a brackets format? How can I do this?
Calling #puts on an array will output each element in that array to its own line. You can output the array, along with brackets, by calling #to_s on the array object:
puts values.to_s
# => ["your", "array", "on", "one", "line"]
Hope that helps!
I have a hash
h = Hash.new{|hsh,key| hsh[key] = [] }
And its values are stored as an array
Iv added to the value array for the key like so:
h[#name] << #age
h[#name] << #grade
and im trying to access the age like this
puts h[:#name][0]
But it doesn't work?
Is there a better way of doing this?
What im trying to do is create is a hash where there is a key which has loads of values:
For example key=>name and values equal age, address, gender etc
IMHO your idea is ok.
The only mistake is.. how you access the hash. No need to add extra colon : before # sign.
Remove colon and it should work as you expect:
puts h[#name][0]
A Hash is a collection of key-value pairs like this: "employee" => "salary". It is similar to an Array, except that indexing is done via arbitrary keys of any object type, not an integer index.
The order in which you traverse a hash by either key or value may seem arbitrary and will generally not be in the insertion order. If you attempt to access a hash with a key that does not exist, the method will return nil.
A hash is used to stored large (or small) amounts of data and access it efficiently. For example lets say you have this as a hash:
prices = {
'orange' => 3.15,
'apple' => 2.25,
'pear' => 3.50
}
Now you want to call the keyword apple and get the prices of those items from some users input:
print 'Enter an item to verify price: '
item = gets.chomp
puts "The price of an #{item}: #{prices[item]}"
# <= The price of an apple: 2.25
That's a basic hash, now lets get into what you're doing, using an Array as a key.
prices = {
'apple' => ['Granny Smith', 'Red'],
'orange' => ['Good', 'Not good'],
'pear' => ['Big', 'Small']
}
print 'Enter an item for a list: '
item = gets.chomp
puts "We have the following #{item}'s available: #{prices[item]}"
# <= We have the following apple's available: ["Granny Smith", "Red"]
Now if we wanted to grab one of the types:
puts prices[item][0]
# <= Granny Smith
puts prices[item][1]
#<= Red
Now lets get into more advanced techniques like you are doing above, you're idea is great and all, but what you need to do is append the information into the hash and when you call #name don't try to call it as a symbol:
h = Hash.new{|hsh,key| hsh[key] = [] }
h[#name] = []
#<= []
h[#name] << ['apple', 'pear']
#<= [["apple", "pear"]]
h[#name] << ['orange', 'apple']
#<= [["apple", "pear"], ["orange", "apple"]]
h[#name].flatten[0]
#<= "apple"
h[#name].flatten[1]
#<= "pear"
h[#name].flatten[1, 2]
#<= ["pear", "orange"]
Alright so what did we do?
h = Hash.new{|hsh,key| hsh[key] = [] }
Created a hash with a value as an empty array.
h[#name] = []
Initialized #name to the empty array
h[#name] << ['apple', 'pear']
Appended an array containing apple, pear to the #name key.
h[#name] << ['orange', 'apple']
Appended a second array containing orange, apple to the array, so when we call h[#name][1] right now it will output the first array appended to it.
h[#name].flatten[0]
Flattened the array into a single array and called the first element of the array.
When you call your key (#name) you don't call it as a symbol, because it's already contained inside of a variable. So all you have to do is call that variable and the value for that key will be output successfully. Hopefully this clarifies a few, things, for more information on hashes check this out: http://www.tutorialspoint.com/ruby/ruby_hashes.htm
As you may know that in Ruby two same strings do not have a same object_id, while two same symbols do. For instance:
irb(main):001:0> :george.object_id == :george.object_id
=> true
irb(main):002:0> "george".object_id == "george".object_id
=> false
However, in my code below, it shows that two strings which have a same value "one" having a same object_id.
class MyArray < Array
def ==(x)
comparison = Array.new()
x.each_with_index{|item, i| comparison.push(item.object_id.equal?(self[i].object_id))}
if comparison.include?(false) then
false
else
true
end
end
end
class MyHash < Hash
def ==(x)
y = Hash[self.sort]
puts y.class
puts y
x = Hash[x.sort]
puts x.class
puts x
puts "______"
xkeys = MyArray.new(x.keys)
puts xkeys.class
puts xkeys.to_s
puts xkeys.object_id
puts xkeys[0].class
puts xkeys[0]
puts xkeys[0].object_id
puts "______"
xvals = MyArray.new(x.values)
puts "______"
selfkeys = MyArray.new(y.keys)
puts selfkeys.class
puts selfkeys.to_s
puts selfkeys.object_id
puts selfkeys[0].class
puts selfkeys[0]
puts selfkeys[0].object_id
puts "______"
selfvals = MyArray.new(y.values)
puts xkeys.==(selfkeys)
puts xvals.==(selfvals)
end
end
a1 = MyHash[{"one" => 1, "two" => 2}]
b1 = MyHash[{"one" => 1, "two" => 2}]
puts a1.==(b1)
And Get
Hash
{"one"=>1, "two"=>2}
Hash
{"one"=>1, "two"=>2}
______
MyArray
["one", "two"]
21638020
String
one
21641920
______
______
MyArray
["one", "two"]
21637580
String
one
21641920
______
true
true
As you can see from the result that 2 String objects with have a same value "one" having a same object_id 21641920, while it's supposed to have different ID. So can anyone give me some hints or tell me how can I get different ID in this case?
Best Regards.
When a String object is used as a key in a Hash, the hash will duplicate and freeze the string internally and will use that copy as its key.
Reference: Hash#store.
As of ruby 2.2 strings used as keys in hash literals are frozen and de-duplicated: the same string will be reused.
This is a performance optimisation: not allocating many copies of the same string means there are fewer objects to allocate and fewer to garbage collect.
Another way to see frozen string literals in action :
"foo".freeze.object_id == "foo".freeze.object_id
Will return true in versions of ruby >= 2.1
I can't tell what's wrong with my code:
def morse_code(str)
string = []
string.push(str.split(' '))
puts string
puts string[2]
end
What I'm expecting is if I use "what is the dog" for str, I would get the following results:
=> ["what", "is", "the", "dog"]
=> "the"
But what I get instead is nil. If I do string[0], it just gives me the entire string again. Does the .split function not break them up into different elements? If anyone could help, that would be great. Thank you for taking the time to read this.
Your code should be :
def morse_code(str)
string = []
string.push(*str.split(' '))
puts string
p string[2]
end
morse_code("what is the dog" )
# >> what
# >> is
# >> the
# >> dog
# >> "the"
str.split(' ') is giving ["what", "is", "the", "dog"], and you are pushing this array object to the array string. Thus string became [["what", "is", "the", "dog"]]. Thus string is an array of size 1. Thus if you want to access any index like 1, 2 so on.., you will get nil. You can debug it using p(it calls #inspect on the array), BUT NOT puts.
def morse_code(str)
string = []
string.push(str.split(' '))
p string
end
morse_code("what is the dog" )
# >> [["what", "is", "the", "dog"]]
With Array, puts works completely different way than p. I am not good to read MRI code always, thus I take a look at sometime Rubinious code. Look how they defined IO::puts, which is same as MRI. Now look the specs for the code
it "flattens a nested array before writing it" do
#io.should_receive(:write).with("1")
#io.should_receive(:write).with("2")
#io.should_receive(:write).with("3")
#io.should_receive(:write).with("\n").exactly(3).times
#io.puts([1, 2, [3]]).should == nil
end
it "writes nothing for an empty array" do
x = []
#io.should_receive(:write).exactly(0).times
#io.puts(x).should == nil
end
it "writes [...] for a recursive array arg" do
x = []
x << 2 << x
#io.should_receive(:write).with("2")
#io.should_receive(:write).with("[...]")
#io.should_receive(:write).with("\n").exactly(2).times
#io.puts(x).should == nil
end
We can now be sure that, IO::puts or Kernel::puts behaves with array just the way, as Rubinious people implemented it. You can now take a look at the MRI code also. I just found the MRI one, look the below test
def test_puts_recursive_array
a = ["foo"]
a << a
pipe(proc do |w|
w.puts a
w.close
end, proc do |r|
assert_equal("foo\n[...]\n", r.read)
end)
end
Working on trying to understand the syntax for calling on different values of a hash.
For example lets say I am trying to delete 'pants' How do go about setting the argument for something like this:
products = {124 => ['shoes', 59.99], 352 => ['shirt', 19.99], 777 => ['pants', 19.87],
667 => ['jacket', 39.99], 898 => ['shoulder_holster', 22.78]}
While writing a menu driven program for this hash I'm including error checking before deleteing or adding a key this is what I have so far:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets.to_s # Get value for argument
while products.has_value?( d + syntax for right here???? )!= true do
puts "This turned out false because product does not exsist!"
d = gets.to_s
end
puts "Congrats your out of the loop"
products.delete(d + again syntax problems ???? )
puts products
end
How do I enter the syntax for the argument if I where to delete pants. Would it be ([d,:number]) I'm not having luck with any resources online with how to delete or add in this scenario. Any help or code example would be appreciated,
Matt
products.to_a.select {|a| a.last.first == 'pants' }
That will get you the record that matches 'pants'.
[[777, ["pants", 19.87]]]
So I think you'll want
while !products.to_a.select {|a| a.last.first == d }.empty?
on your loop then use Dafydd's line to delete the record.
It depends on whether the user is inputing the ID number or the name "pants". If the former:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets # Get value for argument
until products.has_key?(d.to_i)
puts "This turned out false because product does not exsist!"
d = gets
end
puts "Congrats your out of the loop"
products.delete(d.to_i)
puts products
end
If it's "pants", then this is how you want to do it:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets.strip # Need to strip because otherwise the newline will wreck it
until products.find {|key, val| val.first == d}
puts "This turned out false because product does not exsist!"
d = gets.strip
end
puts "Congrats your out of the loop"
products.delete_if {|key, val| val.first == d}
puts products
end
Writing a "delete named product from hash" method
There are shorter ways of doing it, but shooting for clarity I came up with this:
products = {124 => ['shoes', 59.99], 352 => ['shirt', 19.99], 777 => ['pants', 19.87],
667 => ['jacket', 39.99], 898 => ['shoulder_holster', 22.78]}
def wipeProduct(hash, nameToDelete)
hash.each do |i|
key = i[0]
productName = i[1].first
hash.delete(key) if productName==nameToDelete
end
end
puts products.inspect
wipeProduct(products,'pants')
puts products.inspect
wipeProduct(products,'shoulder_holster')
puts products.inspect
bash-3.2$ ruby prod.rb
{352=>["shirt", 19.99], 898=>["shoulder_holster", 22.78], 667=>["jacket", 39.99], 777=>["pants", 19.87], 124=>["shoes", 59.99]}
{352=>["shirt", 19.99], 898=>["shoulder_holster", 22.78], 667=>["jacket", 39.99], 124=>["shoes", 59.99]}
{352=>["shirt", 19.99], 667=>["jacket", 39.99], 124=>["shoes", 59.99]}
I don't know if it's possible for "pants" to occur in the hash in multiple places, but since I used "hash.each(...)", the method wipeProduct(hash, nameToDelete) will test every hash entry.
The input type bug and how to fix it
When you take input, you're assigning the string you captured to d. Here's the proof:
irb(main):010:0> d = gets.to_s
12
=> "12\n"
irb(main):011:0> d.class
=> String
You can convert that string to a Fixnum like this:
irb(main):012:0> d.to_i
=> 12
irb(main):013:0> d.to_i.class
=> Fixnum
All keys in the products hash are Fixnums. Here's the proof:
irb(main):014:0> products.keys.each {|i| puts i.class}
Fixnum
Fixnum
Fixnum
Fixnum
Fixnum
=> [352, 898, 667, 777, 124]
So you need to capture the value for the argument with this line:
d = gets.to_i # Get value for argument
The deletion part of the answer:
From products, you can delete the pants entry programmatically with this:
products.delete(777)
Running it gets you this:
irb(main):003:0> products.delete(777)
=> ["pants", 19.87]
Notice that you supply the key value (in this case 777) to .delete() and that it returns an array consisting of the key and value in that order respectively.
An alternative implementation
I'm not sure if it's safe to modify a hash in a block that's iterating over the key-value pairs in the hash. If it isn't, you can just save up all the keys to be deleted and delete them after iterating over the hash:
def wipeProduct(hash, nameToDelete)
keysToDelete = []
hash.each do |i|
key = i[0]
productName = i[1].first
keysToDelete << key if productName==nameToDelete
end
keysToDelete.each {|key| hash.delete(key) }
end
Here's the neater way to delete the "pants" entry:
def wipeProduct(hash, nameToDelete)
hash.reject!{|key,value| nameToDelete==value.first}
end
The reject! block gets to see each key-value pair, and when it returns true, the key-value supplied will be removed from the hash.
if a == 3 # Loop delete a Product
puts "Delete a Product by its key number"
d = gets
while products.has_key?(d)!= false do
puts "You have selected a key that is not currently in use"
d = gets
end
puts "You have deleted"
products.delete(d)
puts products
end
This is what I ended up doing had some trouble with the until loop so swapped for a while loop though becasue it wouldn't accept newly entered keys for some reason