re-organizing Array (Transpose) - ruby

For an array that looks like:
arr = [["name1","name2","name3"],["address1","address2","address3"],["phone1","phone2","phone3"]]
I would like to re-arrange it so that it looks like:
arr = [["name1","address1","phone1"],["name2","address2","phone2"], ...
Current method is:
name = arr[0]
add = arr[1]
phone = arr[2]
arr = name.zip(add,phone)
which works, but when I have over ten nested arrays within an array, I have ten lines of defining which is which, just to use zip later.
I hope someone can show me a better way of handling this.
EDIT:
I originally had "Phone1","Phone2", as my initial array (uppercase) and "phone1", "phone2" as my transposed array.
This wasn't intended so I edited it, but with my original post Sawa's answer handles the transpose & the UPPERCASE to lowercase.
Also found the documentation here:
http://www.ruby-doc.org/core-2.1.2/Array.html#method-i-transpose

An answer to the original question:
arr.transpose.map{|a| a.map(&:downcase)}
An answer to a different question after OP's edit:
arr.transpose

How about:
arr = arr.shift.zip(*arr)
this code uses the first element of the arr while removing it from arr (via shift), than it uses the splat operator to zip it with the rest of the arrays in the array.

Related

How to sort an array in Ruby

Persoane = []
Nume = gets
Persoane.push Nume.split(",")
puts Persoane.sort
I am trying to get an user to input carachters that get split into substrings which get inserted in an array, then the program would output the strings in alphabetical order. It doesnt seem to work and I just get the array's contents, like so:
PS C:\Users\Lenovo\Desktop\Ruby> ruby "c:\Users\Lenovo\Desktop\Ruby\ruby-test.rb"
Scrie numele la persoane
Andrei,Codrin,Bradea
Andrei
Codrin
Bradea
PS C:\Users\Lenovo\Desktop\Ruby>
you can do this :
Nume = gets
puts Nume.split(",").sort
or in 1 line
array = gets.chomp.split(",").sort
The error is because of your use of push. Let's assume that you define the constant Nume by
Nume='Andrei,Codrin,Bradea'
Then, Nume.split(',') would return the Array ['Andrei', 'Codrin', 'Bradea']. When you do a Persoane.push, the whole array is added to your array Persoane as a single element. Therefore, Persoane contains only one Element, as you can verify when you do a
p Persoane
If you sort a one-element array, the result will also be just that one element - there is nothing to sort.
What you can do is using concat instead of push. This would result in Persoane being a 3-element array which can be sorted.
I'm not sure you need use constants here
If you don't need keep user input and use it somewhere, you can just chain methods like this
persons = gets.chomp.split(",").sort
For something a little different, let's not split at all.
people = gets.scan(/[^,]+/).map(&:strip).sort
This will avoid problems like multiple commas in a row yielding empty strings. Of course, you could also avoid that with:
people = gets.split(/\,+/).map(&:strip).sort

Ruby: Array each loop, save other elements of original array in new array

I am currently trying to compare every element of an array with the others (in Ruby). Those elements are objects of a class. I need to find similarities between them. My idea was to loop through the original array and in this loop creating a new array containing the other elements (not the one of the outer loop) and then loop through this second array and compare every item with the one in the outer each loop.
Here is some pseudocode:
originalArray.each{
|origElement|
tempArray = createNewArray from original array without origElement
tempArray.each{
|differentElement|
Compare origElement with differentElement
}
}
How can I create that tempArray?
I think you should use Array#permutation for this
original_array.permutation(2) { |elements| Compare elements[0] with elements[1] }
First, I want to say bjhaid's answer is beautiful and for your specific instance, it is the one that should be used.
However, I wanted to provide a more general answer that answers the direct question you asked: "How can I create that tempArray?"
If you wanted to delete all values that are equal to the element in the original array, you could simply do:
tempArray = originalArray - [origElement]
However, if you only want to delete that element, you could do:
originalArray.each_with_index {
|origElement, index|
tempArray = originalArray.dup
tempArray.delete_at(index)
tempArray.each{
|differentElement|
Compare origElement with differentElement
}
}
Also, a note on styling. You probably want to use underscores instead of CamelCase for all methods/variables. In the Ruby community, CamelCase is typically reserved for class / module names. You also probably want to keep the "piped-in" variables (called block arguments) on the same line as the beginning of the block. It is certainly not a requirement, but it is an almost universal convention in the Ruby community.
This code snippet would be much more familiar and readable to your typical Ruby dev:
original_array.each_with_index do |orig_element, index|
temp_array = original_array.dup
temp_array.delete_at(index)
temp_array.each do |different_element|
Compare orig_element with different_element
end
end

Is there an equivalent to `Array::sample` for hashes?

I'm looking to extract n random key-value pairs from a hash.
Hash[original_hash.to_a.sample(n)]
For Ruby 2.1,
original_hash.to_a.sample(n).to_h
I don't know of such method. Still you can do something like:
h[h.keys.sample]
If you need to sample more than one element the code will have to be a bit more complicated.
EDIT: to get key value pairs instead of only the value you can do something like:
keys_sample = h.keys.sample(n)
keys_sample.zip(keys_sample.map{|k| h[k])
Reading the top ranked answers, I'd go with it depends:
If you want to sample only one element from the hash, #Ivaylo Strandjev's solution only relies on hash lookup and Array#sample:
hsh[hsh.keys.sample]
To sample multiple hash elements, #sawa's answer leverages Array#to_h:
hsh.to_a.sample(n).to_h
Note that, as #cadlac mentions, hsh.to_a.sample.to_h won't work as expected. It will raise
TypeError: wrong element type String at 0 (expected array)
because Array#sample in this case returns just the element array, and not the array containing the element array.
A workaround is his solution, providing an n = 1 as an argument:
hsh.to_a.sample(1).to_h
PS: not looking for upvotes, only adding it as an explanation for people newer to Ruby.
If your sample has only one element, you could use this:
sample = h.keys.sample
h.select { |k,v| k == sample }
Or if your sample contains more than one element, use this:
n = 2
sample = h.keys.sample(n)
h.select { |k,v| sample.include?(k) }
One way to accomplish this:
rank_hash = {"Listen" => 1, "Download" => 60, "Share" => 150, "Purchase" => 700 }
rank_array = rank_hash.to_a
Than call this to get random array sample of the k/v pair:
rank_array[rand(0..3)]
or this to not hard-code the arrays length:
rank_array[rand(0..(rank_array.length) -1)]
Example:
["Download", 60]

How do you modify array mapping data structure resultant from Ruby map?

I believe that I may be missing something here, so please bear with me as I explain two scenarios in hopes to reconcile my misunderstanding:
My end goal is to create a dataset that's acceptable by Highcharts via lazy_high_charts, however in this quest, I'm finding that it is rather particular about the format of data that it receives.
A) I have found that when data is formatted like this going into it, it draws the points just fine:
[0.0000001240,0.0000000267,0.0000000722, ..., 0.0000000512]
I'm able to generate an array like this simply with:
array = Array.new
data.each do |row|
array.push row[:datapoint1].to_f
end
B) Yet, if I attempt to use the map function, I end up with a result like and Highcharts fails to render this data:
[[6.67e-09],[4.39e-09],[2.1e-09],[2.52e-09], ..., [3.79e-09]]
From code like:
array = data.map{|row| [(row.datapoint1.to_f)] }
Is there a way to coax the map function to produce results in B that more akin to the scenario A resultant data structure?
This get's more involved as I have to also add datetime into this, however that's another topic and I just want to understand this first and what can be done to perhaps further control where I'm going.
Ultimately, EVEN SCENARIO B SHOULD WORK according to the data in the example here: http://www.highcharts.com/demo/spline-irregular-time (press the "View options" button at bottom)
Heck, I'll send you a sucker in the mail if you can fill me in on that part! ;)
You can fix arrays like this
[[6.67e-09],[4.39e-09],[2.1e-09],[2.52e-09], ..., [3.79e-09]]
that have nested arrays inside them by using the flatten method on the array.
But you should be able to avoid generating nested arrays in the first place. Just remove the square brackets from your map line:
array = data.map{|row| row.datapoint1.to_f }
Code
a = [[6.67e-09],[4.39e-09],[2.1e-09],[2.52e-09], [3.79e-09]]
b = a.flatten.map{|el| "%.10f" % el }
puts b.inspect
Output
["0.0000000067", "0.0000000044", "0.0000000021", "0.0000000025", "0.0000000038"]
Unless I, too, am missing something, your problem is that you're returning a single-element array from your block (thereby creating an array of arrays) instead of just the value. This should do you:
array = data.map {|row| row.datapoint1.to_f }
# => [ 6.67e-09, 4.39e-09, 2.1e-09, 2.52e-09, ..., 3.79e-09 ]

Combine array of array into all possible combinations, forward only, in Ruby

I have an array of arrays, like so:
[['1','2'],['a','b'],['x','y']]
I need to combine those arrays into a string containing all possible combinations of all three sets, forward only. I have seen lots of examples of all possible combinations of the sets in any order, that is not what I want. For example, I do not want any of the elements in the first set to come after the second set, or any in the third set to come before the first, or second, and so on. So, for the above example, the output would be:
['1ax', '1ay', '1bx', '1by', '2ax', '2ay', '2bx', '2by']
The number of arrays, and length of each set is dynamic.
Does anybody know how to solve this in Ruby?
Know your Array#product:
a = [['1','2'],['a','b'],['x','y']]
a.first.product(*a[1..-1]).map(&:join)
Solved using a recursive, so-called "Dynamic Programming" approach:
For n-arrays, combine the entries of the first array with each result on the remaining (n-1) arrays
For a single array, the answer is just that array
In code:
def variations(a)
first = a.first
if a.length==1 then
first
else
rest = variations(a[1..-1])
first.map{ |x| rest.map{ |y| "#{x}#{y}" } }.flatten
end
end
p variations([['1','2'],['a','b'],['x','y']])
#=> ["1ax", "1ay", "1bx", "1by", "2ax", "2ay", "2bx", "2by"]
puts variations([%w[a b],%w[M N],['-'],%w[x y z],%w[0 1 2]]).join(' ')
#=> aM-x0 aM-x1 aM-x2 aM-y0 aM-y1 aM-y2 aM-z0 aM-z1 aM-z2 aN-x0 aN-x1 aN-x2
#=> aN-y0 aN-y1 aN-y2 aN-z0 aN-z1 aN-z2 bM-x0 bM-x1 bM-x2 bM-y0 bM-y1 bM-y2
#=> bM-z0 bM-z1 bM-z2 bN-x0 bN-x1 bN-x2 bN-y0 bN-y1 bN-y2 bN-z0 bN-z1 bN-z2
You could also reverse the logic, and with care you should be able to implement this non-recursively. But the recursive answer is rather straightforward. :)
Pure, reduce with product:
a = [['1','2'],['a','b'],['x','y']]
a.reduce() { |acc, n| acc.product(n).map(&:flatten) }.map(&:join)
# => ["1ax", "1ay", "1bx", "1by", "2ax", "2ay", "2bx", "2by"]

Resources