Ruby pushing to a hash - ruby

I have a two part question and I apologize in advance if it is confusing at all. I'm trying to put user input into an empty hash. I know with an array you use the << to push the info to it. Is there a hash equivalent to this?
2nd part: Say I was just looping them the same question until a condition is met. The user input is going to be the value. Is there a way/method to make the key automatically change per the user input? So it would look something like:
{str1 => "example string", str2 => "example string2", str3 => "example string3"}
Or is there a way to have ruby assign a key on its own?
Sorry again if the second part is confusing. I know an array would be better but the little challenge I am working is asking for a hash.

Another way to add element to ruby hash store(key, value)
hash = {}
hash.store("first", 42)
hash #=> {"first"=>42}

With an array you use << to push a single element.
With a hash you are tracking not one element but two (both the key and value).
So for example:
my_key = "foo"
my_val = "bar"
my_hash = {}
my_hash[key] = val
Sure, you can do this in a loop.
I would recommend RubyMonk to learn more about this but their website is down. So I can recommend this gist which shows some examples or simply read the Hash section of any ruby tutorial.

Here are the two ways to add to a Hash
hash[str1] = "example string"
hash.merge!(str1 => "example string")

If you don't care about indexing on a key, as a Hash is intrinsically a key/value store, you probably want a Set:
require 'set'
set = Set.new
set << gets.chomp
A set is like a keyless hash, it's an un-ordered collection of things but with the side benefit that lookups for elements in the set are quick and they're also automatically uniqued, adding the same thing twice has no effect.
The alternative here is to put something in the Hash with the value as the key and any other value as a placeholder:
values = { }
values[input.gets] = true
This is like a Set but is probably less efficient to use if you don't care about values.

Ok, it isn't array so '<<' can't be work.
You should use this:
your_hash = {}
hash_key = "x"
hash_value = "y"
your_hash[:hash_key] = hash_value
It's all.

Related

Unexpected FrozenError when appending elements via <<

I have a hash containing names and categories:
hash = {
'Dog' => 'Fauna',
'Rose' => 'Flora',
'Cat' => 'Fauna'
}
and I want to reorganize it so that the names are grouped by their corresponding category:
{
'Fauna' => ['Dog', 'Cat'],
'Flora' => ['Rose']
}
I am adding each names via <<:
new_hash = Hash.new
hash.each do |name , category|
if new_hash.key?(category)
new_file[category] << name
else
new_hash[category] = name
end
end
But I am being told that this operation is being performed on a frozen element:
`<<' : Can’t modify frozen string (FrozenError)
I suppose this is because each yields frozen objects. How can I restructure this code so the '.each' doesn't provide frozen variables?
I needed to add the first name to an array and then that array to the hash.
new_hash = Hash.new
hash.each do |name , category|
if new_hash.key?(category)
new_file[category] << name
else
new_hash[category] = [name] # <- must be an array
end
end
How can I restructure this code so the '.each' doesn't provide frozen variables?
Short answer: you can't.
Hash#each doesn't "provide frozen variables".
First off, there is no such thing as a "frozen variable". Variables aren't frozen. Objects are. The distinction between variables and objects is fundamental, not just in Ruby but in any programming language (and in fact pretty much everywhere else, too). If I have a sticker with the name "Seamus" on it, then this sticker is not you. It is simply a label that refers to you.
Secondly, Hash#each doesn't provide "variables". In fact, it doesn't provide anything that is not in the hash already. It simply yields the objects that are already in the hash.
Note that, in order to avoid confusion and bugs, strings are automatically frozen when used as keys. So, you can't modify string keys. You can either make sure they are correct from the beginning, or you can create a new hash with new string keys. (You can also add the new keys to the existing hash and delete the old keys, but that is a lot of complexity for little gain.)

How to save an array of information coming from a hash in Ruby

I am new to ruby and don't have much experience with hashes, I have a variable named tweets and it is a hash as such:
{"statuses"=>[{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
I would like to save the array of information as a separate variable in an array. How would I go about this?
Hash's have 2 very nice methods,
hash.values
hash.keys
in your case -
h = {"statuses"=>[{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
p h.values
p.keys
These output arrays of each type. This might be what you want.
Also, this question will very well be closed. 1 Google search reported several Hash to Array SO questions.
Ruby Hash to array of values
Converting Ruby hashes to arrays
If you have a Hash like so:
hash = {:numbers => [1,2,3,4]}
And you need to capture the array into a new variable. You can just access the key and assign it to a new variable like so:
one_to_five = hash[:numbers]
However, note that the new variable actually holds the array that is in the hash. So altering the hash's array alters the new variable's array.
hash[:numbers] << 6
puts one_to_five #=> [1,2,3,4,5,6]
If you use dup, it will create a copy of the array so it will be two separate arrays.
one_to_five = hash[:numbers].dup
hash[:numbers] << 6
puts one_to_five #=> [1,2,3,4,5]
So, in your case:
hash = {'statuses' => [{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
new_array = hash['statuses'].dup
However, it would be interesting to see what it is you are wishing to accomplish with your code, or at least get a little more context, because this may not be the best approach for your final goal. There are a great many things you can do with Arrays and Hashes (and Enumerable) and I would encourage you to read through the documentation on them.

ruby: search an multi-dimensional array for a value and update if needed

Trying to look through an array and see if a particular value is set and if it is, update the numbers attached to it.
Example:
test = [['test',1,2],['watch',1,2],['fish',1,2]]
So I'd like to search this array for 'test' - if it exists, amend the values '1,2', if it doesn't exist, just add the new search term into the array.
New to ruby and having trouble searching inside a multi-dimensional array and getting the key back
I'd go for the hash method suggested in the comments, but if you're really wanting to store your data in the multidimensional array like that I suppose you could do something like:
search_term = "test"
search_item= nil
test.each do |item|
if item.include? search_term
search_item = item
end
end
if search_item.nil?
test << [search_term]
else
search_item << [1,2]
end
I think that would do it (although I'm a little fuzzy on what you were wanting to do after you found the item).

Ruby arrays and non-incremental indexes

I have an array in ruby, and I am setting the index to id of object like below.
My first question is:
This code works:
#array = Array.new(#objects.size)
for i in 0...#objects.size
#array[i] = #objects[i].value
end
but when I do:
#array[#objects[i].id] = #objects[i].value
it says:
undefined method [] for nil::NilClass
I tried putting 100 or 1000 instead of i to make sure it's not about "index out of range", but those worked, I tried converting id to int by using to_i even though it should already be an int, but it still doesn't work. I don't get it.
My second question is:
If I get to make the ids work, does saying Array.new(#objects.size) become usless?
I am not using indexes 0 to size but IDs, so what is happening? Is it initializing indexes 0...size to nil or is it just creating a space for up to x objects?
EDIT:
So I've been told it is better to use Hash for this, and I agree, But I still seem to have the same error in the same situation (just changed Array.new(#objects.size)toHash.new)
Thats not how Arrays work in Ruby. You can however use a hash to do this, and look them up using the method you want:
#lookup_hash = Hash.new
for i in 0...#objects.size
#lookup_hash[#objects[i].id] = #objects[i].value
end
Now you can do:
#lookup_hash[#some_object.id]
And it will return that object's value as you have stored it.
Additional Info
You could also rewrite your loop like this, since you dont need the index anymore:
#lookup_hash = Hash.new
#objects.each do |obj|
#lookup_hash[obj.id] = obj.value
end
A little bit more readable in my opinion.
Your're trying to use an array like a hash. Try this:
Hash[#objects.map{|o| [o.id, o.value] }]
Take a look at the Array and Hash documentations.
#array = #objects.map { |obj| obj.value }
You can, but you don't need to specify the size when creating an array. Anyway, try to use the functional capabilities of Ruby (map, select, inject) instead of C-like imperative loops.
You could use map to do this in a rubyish way:
#array = #objects.map { |o| o.value }

parsing in ruby

I have this Hash:
cookie = {"fbs_138415639544444"=>["\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0
&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340
&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""],
"_play_session"=>["fdasdfasdf"]}
I need to get the substring from right after access_token= to right before &expires. The problem is that the number in the key fbs_138415639544444 changes every time, just the part fbs_ remains constant.
Any idea how to only get:
"138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
This is a common task when decoding parameters and queries in HTML URLs. Here's a little method to break down the parameters into a hash. From there it's easy to get the value you want:
def get_params_hash(params)
Hash[ *params.split('&').map{ |q| q.split('=') }.flatten ]
end
p get_params_hash(cookie['fbs_138415639544444'].first)['"access_token']
# >> "138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
In Ruby 1.9+, hashes retain their insertion order, so if the hash always has the value you want as its first entry, you can use
cookie.keys.first #=> "fbs_138415639544444"
otherwise use:
cookie.keys.select{ |k| k[/^fbs_/] }.first #=> "fbs_138415639544444"
I never code in ruby, but this sounds like a typical task for split function.
you just need to split this
"\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0
&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340
&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""
by & symbol. The first element of result array will be:
"\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
and after split it by =, and the second element of result array should be:
138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt
If you only need the access_key part, then a regex is probably easiest.
cookie["fbs_138415639544444"][0] =~ /access_token\=([-\w\d\|]*)&/
access_key = $1
Here the access_key is in the first capture group and you can get it with $1.
A better option if you'll need other parts of the string (say the session_key), would probably be to use a couple splits and parse the string into it's own hash.
Edit: Just realized you need the key too.
key = cookie.each_key.find { |k| k.start_with? "fbs_" }
Then you can use key to get the value.
Since the key changes, the first step is to get right key:
key = cookie.keys.select {|k| k =~ /^fbs_/}.first
This matches them if they begin with the text "fbs_". The first match is returned.
Next you can get the other value by a few (ugly) splits:
cookie[key].first.split('=')[1].split('&').first
Using a regex might be a bit cleaner, but it depends on what the valid characters are in that string.
Regexs are brittle so I wouldn't use those when the reality is you are parsing query string params in the end so use the CGI lib:
> require 'cgi'
=> true
> cookie = {"fbs_138415639544444"=>["\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""], "_play_session"=>["fdasdfasdf"]}
> CGI.parse(cookie.select {|k,v| k =~ /^fbs_/}.first[1][0])["\"access_token"][0]
=> "138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
This is how i solved the problem...
access_token_key = cookies.keys.find{|item| item.starts_with?('fbs_') }
token = cookies[access_token_key].first
access_token = token.split("&").find{|item| item.include?('access_token') }
fb_access_token = access_token.split("=").find{|item| !item.include?('access_token') }

Resources