How to convert two strings into an array of two in Ruby - ruby

I am looking to extract keys and values from a hash. I manage to retrieve the data but in the wrong format. I am doing the following:
#message_count_series = #messages.collect { |p| "[#{p["created_at"]}, #{p["total_cnt"]}]" }
=> ["[2021-12-02 13:21:19.837233, 3]", "[2021-11-20 13:54:54.846048, 3]"]
What I would like to obtain is:
=> [[2021-12-02 13:21:19.837233, 3], [2021-11-20 13:54:54.846048, 3]]
Just without the quote (not a string).
I tried the following :
#message_opened_series = #messages.collect { |p| ["#{p["created_at"]}, #{p["opened_cnt"]}"] }
=> [["2021-12-02 13:21:19.837233, 1"], ["2021-11-20 13:54:54.846048, 0"]]
Which takes me closer, but now my data are considered a string inside the array.
The following appear to work, but might not be very robust
#message_opened_series = #messages.collect { |p| [DateTime.parse("#{p["created_at"]}"), ("#{p["opened_cnt"]}").to_i] }
=> [[Thu, 02 Dec 2021 13:21:19 +0000, 1], [Sat, 20 Nov 2021 13:54:54 +0000, 0]]
Is there a better way to do this please ?

#message_opened_series =
#messages.collect { |p| [p["created_at"].to_s, p["opened_cnt"]] }
=> [["2021-12-02 13:21:19.837233", 3], ["2021-11-20 13:54:54.846048", 3]]

Related

Remove nil values from array and corresponding entries from reference array

I have two arrays generated from :
#dividends_values = #dividends.historical.map(&:dividend).reverse.last(50)
#dividends_dates = #dividends.historical.map(&:date).reverse.last(50)
The first array is an array of float values and occasional there can be a few nil entries in that. I want to remove those nil entries(which is pretty easy with a compact or something like that), but I also want to move the corresponding entries from the #dividends_dates array.
That is because they the dates array is a 1-1 reference to the values array, so index 0 of array with dates correspondings to index 0 of array with values.
What is a good way to do that?
First, filter by nil. Then break that up into two arrays.
#last_dividends = #dividends.historical.select { |d| d.dividend }
#dividends_values = #last_dividends.map(&:dividend)
#dividends_dates = #last_dividends.map(&:date)
Better yet, turn them into a single array of [[dividend, date], [...]]
#last_dividends = #dividends
.historical
.select { |d| d.dividend }
.map { |d| [d.dividend, d.date] }
First let's create a class-like object for illustration.
Dividend = Struct.new(:value, :date)
historical = [
Dividend.new(nil, "Jan 1"),
Dividend.new(10, "Mar 22"),
Dividend.new(13, "Apr 21"),
Dividend.new(nil, "Aug 7"),
Dividend.new(8, "Oct 11")
]
#=> [#<struct Dividend value=nil, dade="Jan 1">,
# #<struct Dividend value=10, date="Mar 22">,
# #<struct Dividend value=13, date="Apr 21">,
# #<struct Dividend value=nil, date="Aug 7">,
# #<struct Dividend value=8, date="Oct 11">]
Then, for example,
inst = historical[3]
#=> #<struct Dividend value=nil, date="Aug 7">
inst.value
#=> nil
inst.date
#=> "Aug 7"
We may write
historical.filter_map do |inst|
[inst.value, inst.date] unless inst.value.nil?
end.transpose
#=> [[10, 13, 8], ["Mar 22", "Apr 21", "Oct 11"]]
Note that
historical.filter_map do |inst|
[inst.value, inst.date] unless inst.value.nil?
end
#=> [[10, "Mar 22"], [13, "Apr 21"], [8, "Oct 11"]]
See Enumerable#filter_map.
If you have two arrays with corresponding elements, e.g.:
values = [1, nil, 3]
dates = [Date.new(2022, 5, 1), Date.new(2022, 5, 2), Date.new(2022, 5, 3)]
You can turn them into one combined array of [dividend, date] pairs by using transpose:
values_with_dates = [values, dates].transpose
#=> [[1, #<Date: 2022-05-01>], [nil, #<Date: 2022-05-02>], [3, #<Date: 2022-05-03>]]
You can then remove the elements with value of nil via reject!:
values_with_dates.reject! { |value, date| value.nil? }
#=> [[1, #<Date: 2022-05-01>], [3, #<Date: 2022-05-03>]]
And transpose again to separate the pairs:
values_with_dates.transpose
#=> [[1, 3], [#<Date: 2022-05-01>, #<Date: 2022-05-03>]]
The inner arrays can be assigned back to separate variables using Ruby's multiple assignment.
As a one-liner:
values, dates = [values, dates].transpose.reject { |v, _| v.nil? }.transpose

Reducing an array of hashes into new hash

I have an ActiveRecord relation that looks something like this:
[
{
timestamp: Tue, 02 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
organization_id: 1,
all_sales: 10,
direct_sales: 7,
referred_sales: 3,
},
{
timestamp: Wed, 03 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
organization_id: 1,
all_sales: 17,
direct_sales: 8,
referred_sales: 9,
},
{
timestamp: Thu, 04 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
all_sales: 3,
direct_sales: 3,
referred_sales: 0,
}
]
What I'd like to do is create a "sum" of all the keys pertaining to sales (for our purposes here, I don't need timestamp, user_id or organization_id, so basically, I'd like to end with something like this:
{
all_sales: 30
direct_sales: 18
referred_sales: 12
}
Is there an elegant ruby-ish way of doing this? I could easily create a set of variables for each sales category and augment them as I iterate over the original relation, but I wanted to see if the community had a cleaner method. In reality each of these hashes have far more than 3 relevant keys and so I fear that approach will get messy very quickly.
Edit: I also have checked out some other answers to similar questions here on SO (for example: Better way to sum values in an array of hashes), but ideally I wouldn't iterate so many times.
This will work:
arr.each_with_object({}) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] = hash[sym].to_i + obj[sym]
end
end
It's one iteration, you can write the nested loop as 3 different lines, but it's a bit cleaner this way in my opinion.
Note: calling to_i while getting previous value of hash[sym] as initially it is nil and nil.to_i == 0. Alternatively, you can initialize all unknown counts with 0, like this:
arr.each_with_object(Hash.new(0)) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] += obj[sym]
end
end
Since you're starting with an ActiveRecord Relation, you can use pluck to calculate all the sums with SQL and have it return an array with your totals:
SalesModel.pluck('SUM(all_sales)', 'SUM(direct_sales)', 'SUM(referred_sales)')
#=> [30, 18, 12]
Or use functional approach with reduce and merge methods:
keys = %i{all_sales direct_sales referred_sales}
total_sales = items.map {|item| item.select{|key, _| keys.include?(key)}}
.reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}
# total_sales
# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}
Or little bid clearer approach for Ruby 2.5.0 or higher, thanks to #Johan Wentholt
items.map {|item| item.slice(:all_sales, :direct_sales, :referred_sales)}
.reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}
# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}
Use Merge and Reduce function
value = arr.reduce do |h1, h2|
h1.merge(h2) do |k, v1, v2|
[:all_sales, :direct_sales, :referred_sales].include?(k) ? (v1 + v2) : nil
end
end.reject {|_, v| v.nil?}
p value
A couple of more verbose other options (that can be rendered more DRY and general):
result = { all_sales: (array.sum{ |e| e[:all_sales] }), direct_sales: (array.sum{ |e| e[:direct_sales] }), referred_sales: (array.sum{ |e| e[:referred_sales] }) }
or:
result = array.each.with_object(Hash.new(0)) do |h, obj|
obj[:all_sales] += h[:all_sales]
obj[:direct_sales] += h[:direct_sales]
obj[:referred_sales] += h[:referred_sales]
end
To be more DRY and general, starting with the array of the required keys
keys = [:all_sales, :direct_sales, :referred_sales]
The first becomes
keys.map.with_object(Hash.new) { |k, obj| obj[k] = array.sum { |e| e[k] } }
and the second:
array.each.with_object(Hash.new(0)) { |h, obj| keys.each { |k| obj[k] += h[k] } }

Sorting by hash values inside an array of hashes

Im trying to return a list of values inside of of an array of hashes from lowest to highest. I am using the google_drive gem to pull numbers from a google spreadsheet, displaying football information:
Here is where I'm at:
require 'rubygems'
require 'google_drive'
session = GoogleDrive.login("EMAIL", "PASS")
v_qb_w1 = session.spreadsheet_by_key("xxxxxxxx").worksheets[0]
#quarterbacks = [
{ name: v_qb_w1[2, 1], projection: v_qb_w1[2, 2], salary: v_qb_w1[2, 3], dpp: v_qb_w1[2, 4], ppd: v_qb_w1[2, 5] },
{ name: v_qb_w1[3, 1], projection: v_qb_w1[3, 2], salary: v_qb_w1[3, 3], dpp: v_qb_w1[3, 4], ppd: v_qb_w1[3, 5] },
{ name: v_qb_w1[4, 1], projection: v_qb_w1[4, 2], salary: v_qb_w1[4, 3], dpp: v_qb_w1[4, 4], ppd: v_qb_w1[4, 5] }
]
puts "Value:"
#quarterbacks.sort_by do |key, value|
dpp = []
dpp << key[:dpp].to_f.to_s
puts dpp.flatten.sort.reverse
end
That last block was just one of my attempts to try and sort the :dpp key value from lowest to highest. Nothing fails, it just does not change anything. I've tried the grouby_by method and just have no luck arranging my key values
SOLUTION:
#quarterbacks.sort_by! { |qb| qb[:dpp] }
#quarterbacks.each { |qb| puts qb[:dpp] }
First of all, sort_by returns the sorted list, it doesn't sort it in place. That means that just:
#quarterbacks.sort_by { ... }
doesn't do anything useful as you're throwing away the sorted results. You'd need to add an assignment or use sort_by!:
#quarterbacks = #quarterbacks.sort_by { ... }
# or
#quarterbacks.sort_by! { ... }
Then you have understand how the sort_by block works. sort_by sorts using the block's return value, it is more or less like this:
array.map { |e| [ sort_by_block_value[e], e ] }
.sort { |a, b| a.first <=> b.first }
.map { |e| e.last }
so your block needs to return something sensible rather than the nil that puts returns:
#quarterbacks.sort_by! { |q| q[:dpp] }
Try this
#quarterbacks.sort_by!{|qb| qb[:dpp]}
You are trying to sort an Array. Right now you passing a Hash(k) and nil(v) because each quarterback is stored as a Hash so there is no key => value association in the Array. Also puts will return nil so you are telling it to sort nil against nil repetitively.
The code above will sort the Array of Hashes by the :dpp attribute of each Hash which seems like what you are asking for. The ! in this case means it will alter the receiver altering the #quarterbacks instance variable to be sorted in place.

How to split a string in x equal pieces in ruby

I have a string in ruby like this:
str = "AABBCCDDEEFFGGHHIIJJ01020304050607080910"
# 20 letters and 20 numbers in this case
I want to split this in half, which I can do like this:
str[0, str.length/2]
or
str.split(0, str.length/2)
After that, I need to make arrays with the chars but with length 2 for each element like this:
["AA", "BB", "CC", "DD", "EE", "FF", "GG", "HH", "II", "JJ"],
[01, 02, 03, 04, 05, 06, 07, 08, 09, 10]
The problem is, I can't find a concise way to convert this string. I can do something like this
arr = []
while str.length > 0 do
arr << str[0, 1]
str[0, 1] = ""
end
but I rather want something like str.split(2), and the length of the string may change anytime.
How about this?
str.chars.each_slice(2).map(&:join)
You could use the scan method:
1.9.3p194 :004 > a = 'AABBCCDDEEC'
=> "AABBCCDDEEC"
1.9.3p194 :005 > a.scan(/.{1,2}/)
=> ["AA", "BB", "CC", "DD", "EE", "C"]
2.1.0 :642 > "d852".scan(/../)
=> ["d8", "52"]

What is the best way to convert an array to a hash in Ruby

In Ruby, given an array in one of the following forms...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
...what is the best way to convert this into a hash in the form of...
{apple => 1, banana => 2}
Simply use Hash[*array_variable.flatten]
For example:
a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts "h1: #{h1.inspect}"
a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts "h2: #{h2.inspect}"
Using Array#flatten(1) limits the recursion so Array keys and values work as expected.
NOTE: For a concise and efficient solution, please see Marc-André Lafortune's answer below.
This answer was originally offered as an alternative to approaches using flatten, which were the most highly upvoted at the time of writing. I should have clarified that I didn't intend to present this example as a best practice or an efficient approach. Original answer follows.
Warning! Solutions using flatten will not preserve Array keys or values!
Building on #John Topley's popular answer, let's try:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
This throws an error:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
The constructor was expecting an Array of even length (e.g. ['k1','v1,'k2','v2']). What's worse is that a different Array which flattened to an even length would just silently give us a Hash with incorrect values.
If you want to use Array keys or values, you can use map:
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
This preserves the Array key:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
The best way is to use Array#to_h:
[ [:apple,1],[:banana,2] ].to_h #=> {apple: 1, banana: 2}
Note that to_h also accepts a block:
[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] }
# => {apple: "I like apples", banana: "I like bananas"}
Note: to_h accepts a block in Ruby 2.6.0+; for early rubies you can use my backports gem and require 'backports/2.6.0/enumerable/to_h'
to_h without a block was introduced in Ruby 2.1.0.
Before Ruby 2.1, one could use the less legible Hash[]:
array = [ [:apple,1],[:banana,2] ]
Hash[ array ] #= > {:apple => 1, :banana => 2}
Finally, be wary of any solutions using flatten, this could create problems with values that are arrays themselves.
Update
Ruby 2.1.0 is released today. And I comes with Array#to_h (release notes and ruby-doc), which solves the issue of converting an Array to a Hash.
Ruby docs example:
[[:foo, :bar], [1, 2]].to_h # => {:foo => :bar, 1 => 2}
Edit: Saw the responses posted while I was writing, Hash[a.flatten] seems the way to go.
Must have missed that bit in the documentation when I was thinking through the response. Thought the solutions that I've written can be used as alternatives if required.
The second form is simpler:
a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }
a = array, h = hash, r = return-value hash (the one we accumulate in), i = item in the array
The neatest way that I can think of doing the first form is something like this:
a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }
You can also simply convert a 2D array into hash using:
1.9.3p362 :005 > a= [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
1.9.3p362 :006 > h = Hash[a]
=> {1=>2, 3=>4}
Summary & TL;DR:
This answer hopes to be a comprehensive wrap-up of information from other answers.
The very short version, given the data from the question plus a couple extras:
flat_array = [ apple, 1, banana, 2 ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [ apple, 1, banana ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana ] ] # count=2 of either k or k,v arrays
# there's one option for flat_array:
h1 = Hash[*flat_array] # => {apple=>1, banana=>2}
# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0 => {apple=>1, banana=>2}
h2b = Hash[nested_array] # => {apple=>1, banana=>2}
# ok if *only* the last value is missing:
h3 = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4 = Hash[incomplete_n] # or .to_h => {apple=>1, banana=>nil}
# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3 # => false
h3 == h4 # => true
Discussion and details follow.
Setup: variables
In order to show the data we'll be using up front, I'll create some variables to represent various possibilities for the data. They fit into the following categories:
Based on what was directly in the question, as a1 and a2:
(Note: I presume that apple and banana were meant to represent variables. As others have done, I'll be using strings from here on so that input and results can match.)
a1 = [ 'apple', 1 , 'banana', 2 ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input
Multi-value keys and/or values, as a3:
In some other answers, another possibility was presented (which I expand on here) – keys and/or values may be arrays on their own:
a3 = [ [ 'apple', 1 ],
[ 'banana', 2 ],
[ ['orange','seedless'], 3 ],
[ 'pear', [4, 5] ],
]
Unbalanced array, as a4:
For good measure, I thought I'd add one for a case where we might have an incomplete input:
a4 = [ [ 'apple', 1],
[ 'banana', 2],
[ ['orange','seedless'], 3],
[ 'durian' ], # a spiky fruit pricks us: no value!
]
Now, to work:
Starting with an initially-flat array, a1:
Some have suggested using #to_h (which showed up in Ruby 2.1.0, and can be backported to earlier versions). For an initially-flat array, this doesn't work:
a1.to_h # => TypeError: wrong element type String at 0 (expected array)
Using Hash::[] combined with the splat operator does:
Hash[*a1] # => {"apple"=>1, "banana"=>2}
So that's the solution for the simple case represented by a1.
With an array of key/value pair arrays, a2:
With an array of [key,value] type arrays, there are two ways to go.
First, Hash::[] still works (as it did with *a1):
Hash[a2] # => {"apple"=>1, "banana"=>2}
And then also #to_h works now:
a2.to_h # => {"apple"=>1, "banana"=>2}
So, two easy answers for the simple nested array case.
This remains true even with sub-arrays as keys or values, as with a3:
Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
a3.to_h # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
But durians have spikes (anomalous structures give problems):
If we've gotten input data that's not balanced, we'll run into problems with #to_h:
a4.to_h # => ArgumentError: wrong array length at 3 (expected 2, was 1)
But Hash::[] still works, just setting nil as the value for durian (and any other array element in a4 that's just a 1-value array):
Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
Flattening - using new variables a5 and a6
A few other answers mentioned flatten, with or without a 1 argument, so let's create some new variables:
a5 = a4.flatten
# => ["apple", 1, "banana", 2, "orange", "seedless" , 3, "durian"]
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"]
I chose to use a4 as the base data because of the balance problem we had, which showed up with a4.to_h. I figure calling flatten might be one approach someone might use to try to solve that, which might look like the following.
flatten without arguments (a5):
Hash[*a5] # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)
At a naïve glance, this appears to work – but it got us off on the wrong foot with the seedless oranges, thus also making 3 a key and durian a value.
And this, as with a1, just doesn't work:
a5.to_h # => TypeError: wrong element type String at 0 (expected array)
So a4.flatten isn't useful to us, we'd just want to use Hash[a4]
The flatten(1) case (a6):
But what about only partially flattening? It's worth noting that calling Hash::[] using splat on the partially-flattened array (a6) is not the same as calling Hash[a4]:
Hash[*a6] # => ArgumentError: odd number of arguments for Hash
Pre-flattened array, still nested (alternate way of getting a6):
But what if this was how we'd gotten the array in the first place?
(That is, comparably to a1, it was our input data - just this time some of the data can be arrays or other objects.) We've seen that Hash[*a6] doesn't work, but what if we still wanted to get the behavior where the last element (important! see below) acted as a key for a nil value?
In such a situation, there's still a way to do this, using Enumerable#each_slice to get ourselves back to key/value pairs as elements in the outer array:
a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]]
Note that this ends up getting us a new array that isn't "identical" to a4, but does have the same values:
a4.equal?(a7) # => false
a4 == a7 # => true
And thus we can again use Hash::[]:
Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]
But there's a problem!
It's important to note that the each_slice(2) solution only gets things back to sanity if the last key was the one missing a value. If we later added an extra key/value pair:
a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # multi-value key
# ["durian"], # missing value
# ["lychee", 4]] # new well-formed item
a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]
a7_plus = a6_plus.each_slice(2).to_a
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # so far so good
# ["durian", "lychee"], # oops! key became value!
# [4]] # and we still have a key without a value
a4_plus == a7_plus # => false, unlike a4 == a7
And the two hashes we'd get from this are different in important ways:
ap Hash[a4_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => nil, # correct
"lychee" => 4 # correct
}
ap Hash[a7_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => "lychee", # incorrect
4 => nil # incorrect
}
(Note: I'm using awesome_print's ap just to make it easier to show the structure here; there's no conceptual requirement for this.)
So the each_slice solution to an unbalanced flat input only works if the unbalanced bit is at the very end.
Take-aways:
Whenever possible, set up input to these things as [key, value] pairs (a sub-array for each item in the outer array).
When you can indeed do that, either #to_h or Hash::[] will both work.
If you're unable to, Hash::[] combined with the splat (*) will work, so long as inputs are balanced.
With an unbalanced and flat array as input, the only way this will work at all reasonably is if the last value item is the only one that's missing.
Side-note: I'm posting this answer because I feel there's value to be added – some of the existing answers have incorrect information, and none (that I read) gave as complete an answer as I'm endeavoring to do here. I hope that it's helpful. I nevertheless give thanks to those who came before me, several of whom provided inspiration for portions of this answer.
Appending to the answer but using anonymous arrays and annotating:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
Taking that answer apart, starting from the inside:
"a,b,c,d" is actually a string.
split on commas into an array.
zip that together with the following array.
[1,2,3,4] is an actual array.
The intermediate result is:
[[a,1],[b,2],[c,3],[d,4]]
flatten then transforms that to:
["a",1,"b",2,"c",3,"d",4]
and then:
*["a",1,"b",2,"c",3,"d",4] unrolls that into
"a",1,"b",2,"c",3,"d",4
which we can use as the arguments to the Hash[] method:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
which yields:
{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
if you have array that looks like this -
data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]
and you want the first elements of each array to become the keys for the hash and the rest of the elements becoming value arrays, then you can do something like this -
data_hash = Hash[data.map { |key| [key.shift, key] }]
#=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}
Not sure if it's the best way, but this works:
a = ["apple", 1, "banana", 2]
m1 = {}
for x in (a.length / 2).times
m1[a[x*2]] = a[x*2 + 1]
end
b = [["apple", 1], ["banana", 2]]
m2 = {}
for x,y in b
m2[x] = y
end
For performance and memory allocation concerns please check my answer to Rails mapping array of hashes onto single hash where I bench-marked several solutions.
reduce / inject can be the fastest or the slowest solution depending on which method you use it which.
If the numeric values are seq indexes, then we could have simpler ways...
Here's my code submission, My Ruby is a bit rusty
input = ["cat", 1, "dog", 2, "wombat", 3]
hash = Hash.new
input.each_with_index {|item, index|
if (index%2 == 0) hash[item] = input[index+1]
}
hash #=> {"cat"=>1, "wombat"=>3, "dog"=>2}

Resources