Converting array elements to hashes - ruby

I have an array with the following elements:
results = ["test=complete", "test2=passed", "test3=failed", "test4=successful"]
I want to convert this array into a hash with the following keys and values:
results_hash = {"test": "complete", "test2": "passed", "test3": "failed", "test4": "successful"}
How can I do this? Not sure if best to use set, hash, etc.

In Ruby 2.6:
results.to_h{|s| s.split("=").then{|k, v| [k.to_sym, v]}}
# => {:test=>"complete", :test2=>"passed", :test3=>"failed", :test4=>"successful"}

results = ["test=complete", "test2=passed", "test3=failed", "test4=successful"]
results_hash = results.map{|str| str.split("=") }.to_h
# => {"test"=>"complete", "test2"=>"passed", "test3"=>"failed", "test4"=>"successful"}
Edit: As #sawa comments, the keys should be symbols. This is a way:
results.map{|str| a,b = str.split("="); [a.to_sym, b] }.to_h

This is my solution:
array = ['test=passed', 'test=failed']
num_of_elements = array.length
x = array[0].split("")
y = array[1].split("")
array = x + y
x.delete_if {|x| x.match('=')}
y.delete_if {|x| x.match('=')}
test_arr = x[0..3].join()
fail = x[4..9].join()
test_arr_2 = y[0..3].join()
fail_2 = y[4..9].join()
results = {}
results.compare_by_identity
results["#{test_arr}"] = fail
results["#{test_arr_2}"] = fail_2
puts results

Related

Ruby - Merge two hashes and maintain ordered keys

I have two hashes:
a = {"0"=>"name", "1"=>"email"}
b = {"0"=>"source", "1"=>"info", "2"=>"extra", "3"=>"name"}
I want a hash created by doing the following:
1) When the two hashes contain identical values, keep value of original hash and discard value of second hash.
2) When the values of second hash are not in first hash, just add to the end of new hash, making sure that the key is ordered.
with this result:
{"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}
I did it this ugly way:
l1 = a.keys.length
l2 = b.keys.length
max = l1 > l2 ? l1 : l2
counter = l1
result = {}
max.times do |i|
unless a.values.include? b[i.to_s]
result[counter.to_s] = b[i.to_s]
counter += 1
end
end
a.merge!(result)
Is there a built-in ruby method or utility that could achieve this same task in a cleaner fashion?
(a.values + b.values).uniq.map.with_index{|v, i| [i.to_s, v]}.to_h
# => {"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}
First create an array containing the values in the hash. This can be accomplished with the concat method. Now that we have an array, we can call the uniq method to retrieve all unique values. This also preserves the order.
a = { "0" => "name", "1" => "email" }
b = { "0" => "source", "1" => "info", "2" => "extra", "3" => "name" }
values = a.values.concat(b.values).uniq
A shortcut to generating a hash in Ruby is with this trick.
Hash[[*0..values.length-1].zip(values)]
Output:
{0=>"name", 1=>"email", 2=>"source", 3=>"info", 4=>"extra"}
a = {"0"=>"name", "1"=>"email"}
b = {"0"=>"source", "1"=>"info", "2"=>"extra", "3"=>"name"}
key = (a.size-1).to_s
#=> "1"
b.each_value.with_object(a) { |v,h| (h[key.next!] = v) unless h.value?(v) }
#=> {"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}

Preserving key value pairs for duplicate keys when merging two Arrays to a hash

I have two arrays in ruby:
array_one = ["farmer_joe", "farmer_judy", "farmer_crazy_eyes", "farmer_joe"]
array_two = ["pigs", "chickens", "elephants", "cows"]
If I use the zip function I lose the duplicate value, key pair for Farmer Joe.
hash_one = Hash[array_one.zip array_two]
=> {"farmer_joe"=>"cows", "farmer_judy"=>"chickens", "farmer_crazy_eyes"=>"elephants"}
Ideally I want a function that allows me to overcome this in a nifty ruby flavoured oneline method. perhaps something like this that merges duplicate keys and adds their values to an array.
hash_one = Hash[array_one.super_special_zip array_two]
=> {"farmer_joe"=>["pigs","cows"], "farmer_judy"=>["chickens"], "farmer_crazy_eyes"=>["elephants"]}
Is there such a super_special_zip method? Or is there a good reason why this is a fools errand in the first place?
There are three standard ways of doing this.
a1 = ["farmer_joe", "farmer_judy", "farmer_crazy_eyes", "farmer_joe"]
a2 = ["pigs", "chickens", "elephants", "cows"]
pairs = a1.zip(a2) # or [a1,a2].transpose
#=> [["farmer_joe", "pigs"], ["farmer_judy", "chickens"],
# ["farmer_crazy_eyes", "elephants"], ["farmer_joe", "cows"]]
1. Use Hash.new to create a hash with a default value of an empty array
pairs.each_with_object(Hash.new { |h,k| h[k]=[] }) { |(f,l),h| h[f] << l }
# => {"farmer_joe"=>["pigs", "cows"], "farmer_judy"=>["chickens"],
# "farmer_crazy_eyes"=>["elephants"]}
A variant of this (which tends to be slightly faster) is:
pairs.each_with_object({}) { |(f,l),h| (h[f] ||= []) << l }
2. Use the form of Hash#update (aka merge!) that takes a block to determine the values of keys present in both hashes being merged
pairs.each_with_object({}) { |(f,l),h| h.update(f=>[l]) { |_,o,n| o+n } }
#=> {"farmer_joe"=>["pigs", "cows"], "farmer_judy"=>["chickens"],
# "farmer_crazy_eyes"=>["elephants"]}
3. Use Enumerable#group_by
h = pairs.group_by(&:first)
#=> {"farmer_joe"=>[["farmer_joe", "pigs"], ["farmer_joe", "cows"]],
# "farmer_judy"=>[["farmer_judy", "chickens"]],
# "farmer_crazy_eyes"=>[["farmer_crazy_eyes", "elephants"]]}
h.keys.each { |k| h[k] = h[k].map(&:last) }
h
#=> {"farmer_joe"=>["pigs", "cows"], "farmer_judy"=>["chickens"],
# "farmer_crazy_eyes"=>["elephants"]}
There are many alternative to the last two lines, one being:
h.merge(h) { |*_,v| v.map(&:last) }
One way to do it
array_one = ["farmer_joe", "farmer_judy", "farmer_crazy_eyes", "farmer_joe"]
array_two = ["pigs", "chickens", "elephants", "cows"]
hash_one = {}
array_one.each_with_index do |farmer,i|
if hash_one.has_key?(farmer)
hash_one[farmer] << array_two[i]
else
hash_one[farmer] = [array_two[i]]
end
end
hash_one # => {"farmer_joe"=>["pigs", "cows"], "farmer_judy"=>["chickens"], "farmer_crazy_eyes"=>["elephants"]}
And yet another way to do that would be ( no rails )
a1 = ["farmer_joe", "farmer_judy", "farmer_crazy_eyes", "farmer_joe"]
a2= ["pigs", "chickens", "elephants", "cows"]
a1.zip(a2).group_by(&:first).map{|key, value| [key, value.map(&:last)]}.to_h
# => {"farmer_joe"=>["pigs", "cows"], "farmer_judy"=>["chickens"], "farmer_crazy_eyes"=>["elephants"]}

splitting an array into a hash

Im looking to split an array of strings and creating a hash out of it.
I have an algorithm that splits a string into an array by commas this:1, is:1, a:1, string:1
def split_answer_to_hash(str)
words = str.split(',')
answer = {}
words.each do |w|
a = w.split(':')
h = Hash[ *a.collect { |v| [ v, a[1] ] } ]
answer = h
end
answer
end
What I need to do now is to make the left side of the colon the key to the hash and the right side of the colon the value of the hash. example: {"this" =>1, "is"=>1, "a"=>1, "string"=>1 }
*a.collect is iterating through the array and making the value another key. How can I go about this with out that happening?
The easiest way is:
string = 'this:1, is:1, a:1, string:1'
hash = Hash[*string.split(/:|,/)]
#=> {"this"=>"1", " is"=>"1", " a"=>"1", " string"=>"1"}
Having just one answer to this question just won't do:
str = "this:1, is:1, a:1, string:1"
Hash[str.scan(/(?:([^:]+):(\d+)(?:,\s)?)/)]
.tap { |h| h.keys.each { |k| h[k] = h[k].to_i } }
#=> {"this"=>1, "is"=>1, "a"=>1, "string"=>1}
Object#tap is used merely to convert the values from strings to integers. If you'd prefer:
h = Hash[str.scan(/(?:([^:]+):(\d+)(?:,\s)?)/)]
h.keys.each { |k| h[k] = h[k].to_i }
h
#=> {"this"=>1, "is"=>1, "a"=>1, "string"=>1}
For Ruby 2.1, you can replace Hash[arr] with arr.to_h.

Building a hash out of hash

I'm trying to write my code more compact. I have three hashes. The first hash (#hash) is a collection of sub-hashes (value_1, value_2)
#hash = {
"Key" => ["value_1", "value_2"]
}
#value_1 = {
"Foo" => ["bar_1", "bar_2"]
}
#value_2 = {
"Foo2" => ["bar2_1", "bar2_2"]
}
Now, in my haml-view i'm trying to make something like this:
- i = 0
- #hash.each_value do |value|
- #value_[i].each_pair do |k, v|
= k
= v[0]
- i = i +1
I don't want to write one hash after the other. It's a bit similar to making a symbol out of a string, where you can write somthing like "value_#{i}".to_sym. I hope, somebody can follow and help me.
#hashes = [
{
"Foo" => ["bar_1", "bar_2"]
},
{
"Foo2" => ["bar2_1", "bar2_2"]
}
]
Then
#hashes.each do |v|
v.each_pair do |k,v|
= k
= v[0]
And if you need the index use each_with_index.
EDIT
Try this:
- #hash.each_value do |value|
- value.each do |v|
- instance_variable_get(:"##{v}").each_pair do |k, val|
= k
= val[0]
API

Looping and creating an array in Ruby

Trying to port some old PHP code to Ruby and missing some key info on creating arrays in Ruby.
The PHP code:
foreach ($results as $r) {
$array[$r['column']][] = $r
}
Is this the simplest way to do it in Ruby? Do I have to initialize the second array?
#array = []
result.each do |r|
#array[r.cron_column] = []
#array[r.cron_column] << r
end
I figure this is a simple syntax issue, but my Googling has turned up empty. Thanks for your help!
You are indexing into an empty array, so that will always return nil. nil does not define the << operator, so you get an error. You need to initialize the value at array[index] if you want to use the << operator.
I am assuming you want an array of arrays, so you can use this instead which will initialize the value at items[index] to an empty array if it is nil before pushing the value onto it
items = []
array.each do |r|
(items[r.column] ||= []) << r
end
The only change here is that, if items[r.column] returns nil it will be set equal to an empty array, otherwise nothing will be done. If you really just want to set the value at items[index] to r, just use the = operator.
Are you sure you need an array as output? it would appear a hash would be more convenient; moreover, it's way easier to build in your scenario (which is usually a sign you are in the correct path):
# example
results = [
OpenStruct.new(:x => 1, :cron_column => 0),
OpenStruct.new(:x => 2, :cron_column => 1),
OpenStruct.new(:x => 3, :cron_column => 1),
]
#array = results.group_by(&:cron_column)
# {0=>[#<OpenStruct x=1, cron_column=0>],
# 1=>[#<OpenStruct x=2, cron_column=1>, #<OpenStruct x=3, cron_column=1>]}
If cron_column "has no holes" (that's it, you have values from 0 to N), you can easily create an array with this same idea: results.group_by(&:cron_column).sort.map { |k, v| v } or results.group_by(&:cron_column).sort.map(&:last), as you prefer.
array = results.inject([]) { |m, r| m[r.column] = r; m }
Update: oh, e1[] = e2 adds a new array element in PHP, so tokland is right, in which case:
array = results.inject([]) { |m, r| (m[r.column] ||= []) << r; m }

Resources