Zip all array values of hash - ruby

I'd like to zip all the array values of a hash. I know there's a way to zip arrays together. I'd like to do that with the values of my hash below.
current_hash = {:a=>["k", "r", "u"],
:b=>["e", " ", "l"],
:c=>["d", "o", "w"],
:d=>["e", "h"]
}
desired_outcome = "keder ohulw"
I have included my desired outcome above.

current_hash.values.then { |first, *rest| first.zip(*rest) }.flatten.compact.join
An unfortunate thing with Ruby zip is that the first enumerable needs to be the receiver, and the others need to be parameters. Here, I use then, parameter deconstruction and splat to separate the first enumerable from the rest. flatten gets rid of the column arrays, compact gets rid of the nil (though it's not really necessary as join will ignore it), and join turns the array into the string.
Note that Ruby zip will stop at length of the receiver; so if :a is shorter than the others, you will likely have a surprising result. If that is a concern, please update with an example that reflects that scenario, and the desired outcome.

Here I'm fleshing out #Amadan's remark below the horizontal line in is answer. Suppose:
current_hash = { a:["k","r"], b:["e"," ","l"], c:["d","o","w"], d:["e", "h"] }
and you wished to return "keder ohlw". If you made ["k","r"] and [["e"," ","l"], ["d","o","w"], ["e", "h"]] zip's receiver and argument, respectively, you would get "keder oh", which omits "l" and "w". (See Array#zip, especially the 3rd paragraph.)
To include those strings you would need to fill out ["k","r"] with nils to make it as long as the longest value, or make zip's receiver an array of nils of the same length. The latter approach can be implemented as follows:
vals = current_hash.values
#=> [["k", "r"], ["e", " ", "l"], ["d", "o", "w"], ["e", "h"]]
([nil]*vals.map(&:size).max).zip(*vals).flatten.compact.join
#=> "keder ohlw"
Note:
a = [nil]*vals.map(&:size).max
#=> [nil, nil, nil]
and
a.zip(*vals)
#=> [[nil, "k", "e", "d", "e"],
# [nil, "r", " ", "o", "h"],
# [nil, nil, "l", "w", nil]]
One could alternatively use Array#transpose rather than zip.
vals = current_hash.values
idx = (0..vals.map(&:size).max-1).to_a
#=> [0, 1, 2]
vals.map { |a| a.values_at(*idx) }.transpose.flatten.compact.join
#=> "keder ohlw"
See Array#values_at. Note:
a = vals.map { |a| a.values_at(*idx) }
#=> [["k", "r", nil],
# ["e", " ", "l"],
# ["d", "o", "w"],
# ["e", "h", nil]]
a.transpose
#=> [["k", "e", "d", "e"],
# ["r", " ", "o", "h"],
# [nil, "l", "w", nil]]

Related

using each_slice and sort Ruby

I need to reorganize an array into slices of 2 elements and then sort each slice alphabetically using each_slice
I've managed to get the each_slice correctly but I can seem to then sort each sub array.
What am I doing wrong here?
array.each_slice(2).to_a { |el| el = el.sort}
You just need to create a new array with the output that you want.
For instance:
# $ array = ["b", "a", "d", "c", "k", "l", "p"]
arr = []
array.each_slice(2) { |el| arr << el.sort}
# $ arr
# => [["a", "b"], ["c", "d"], ["k", "l"], ["p"]]
EDIT:
Pointed in the comments (by #mu is too short), you can also do:
arr = array.each_slice(2).map(&:sort)

How to split an already split array ruby

I have this function in Ruby
def translate word
vowels=["a","e","I","O","U"]
i=1.to_i
sentense=word.split(" ").to_a
puts sentense if sentense.length >=1
sentense.split("")
puts sentense
end
I have this phrase "this is a test phrase " and at first I want to create an array that looks like:
["this","is","a", "test", "phrase"]
Then I want to create another array it to look like:
[["t","h","i","s"],["i","s"],["a"],["t","e","s","t"],["p","h","r","a","s","e"].
I tried
sentense=word.split(" ").to_a
new_array=sentense.split("").to_a
but it didn't work
You could use String#split, Enumerable#map and String#chars:
p "this is a test phrase".split.map(&:chars)
# => [["t", "h", "i", "s"], ["i", "s"], ["a"], ["t", "e", "s", "t"], ["p", "h", "r", "a", "s", "e"]]
string.split(' ') could be written as string.split, so you can omit passing the whitespace in parenthesis.
And this also gives you an array, there's no need to use to_a, you'll have an array like ["this", "is", "a", "test", "phrase"], so you can use map to get a new array and for each element inside an array of its characters by using .split('') or .chars.
def chop_up(str)
str.strip.each_char.with_object([[]]) { |c,a| c == ' ' ? (a << []) : a.last << c }
end
chop_up "fee fi fo fum"
#=> [["f", "e", "e"], ["f", "i"], ["f", "o"], ["f", "u", "m"]]
chop_up " fee fi fo fum "
#=> [["f", "e", "e"], ["f", "i"], ["f", "o"], ["f", "u", "m"]]
chop_up "feefifofum "
#=> [["f", "e", "e", "f", "i", "f", "o", "f", "u", "m"]]
chop_up ""
#=> [[]]

How to extract each individual combination from a flat_map?

I'm fairly new to ruby and it's my first question here on stackoverflow so pardon me if I'm being a complete noob.
The code which i am working with contains this line -
puts (6..6).flat_map{|n| ('a'..'z').to_a.combination(n).map(&:join)}
What the code does is that its starts printing each of the combinations starting from "abcdef" and continues till the end (which i have never seen as it has 26^6 combinations).
Of course having an array of that size (26^6) is unimaginable hence I was wondering if there is any way by which i can get next combination in a variable, work with it, and then continue on to the next combination ?
For example I calculate the first combination as "abcdef" and store it in a variable 'combo' and use that variable somewhere and then the next combination is calculated and "abcdeg" is stored in 'combo' and hence the loop continues ?
Thanks
(6..6).flat_map { |n| ... } doesn't do much. Your code is equivalent to:
puts ('a'..'z').to_a.combination(6).map(&:join)
To process the values one by one, you can pass a block to combination:
('a'..'z').to_a.combination(6) do |combo|
puts combo.join
end
If no block is given, combination returns an Enumerator that can be iterated by calling next:
enum = ('a'..'z').to_a.combination(6)
#=> #<Enumerator: ["a", "b", "c", ..., "w", "x", "y", "z"]:combination(6)>
enum.next
#=> ["a", "b", "c", "d", "e", "f"]
enum.next
#=> ["a", "b", "c", "d", "e", "g"]
enum.next
#=> ["a", "b", "c", "d", "e", "h"]
Note that ('a'..'z').to_a.combination(6) will "only" yield 230,230 combinations:
('a'..'z').to_a.combination(6).size
#=> 230230
As opposed to 26 ^ 6 = 308,915,776. You are probably looking for repeated_permutation:
('a'..'z').to_a.repeated_permutation(6).size
#=> 308915776
Another way to iterate from "aaaaaa" to "zzzzzz" is a simple range:
('aaaaaa'..'zzzzzz').each do |combo|
puts combo
end
Or manually by calling String#succ: (this is what Range#each does under the hood)
'aaaaaa'.succ #=> "aaaaab"
'aaaaab'.succ #=> "aaaaac"
'aaaaaz'.succ #=> "aaaaba"

Sort hash by key, ignoring accents

I have a hash like:
dict = {
"someKey" => [ ... ],
"anotherKey" => [ ... ],
"yetAnōtherKéy" => [ ... ]
}
I want a new hash by sorting the original. Sorting should ignore the accents (done by replacing the accented characters with their un-accented version),
replacements = [
["ā", "a"], ["á", "a"], ["à", "a"], ["ǎ", "a"],
["ō", "o"], ["ó", "o"], ["ò", "o"], ["ǒ", "o"],
["ī", "i"], ["í", "i"], ["ì", "i"], ["ǐ", "i"],
["ē", "e"], ["é", "e"], ["è", "e"], ["ě", "e"],
["ū", "u"], ["ú", "u"], ["ù", "u"], ["ǔ", "u"]
]
but the keys in the resulting hash should keep the original keys. How is that possible?
I tried
dict = Hash[dict.sort_by{|k,v| k}]
This works and does sort the hash. However, it doesn't ignore the accents, i.e., the words starting with an accented character go to the bottom.
Another attempt is:
replacements.each {|replacement| z.gsub!(replacement[0], replacement[1])}
Code corrected following Cary Swoveland's comment.
replacements = Hash.new{|_, k| k}.merge(replacements.to_h)
dict.sort_by{|k,_| k.gsub(/./, replacements)}.to_h
Use the unidecoder gem.
require 'unidecoder'
Hash[dict.sort_by{|k,v| k.to_ascii }]
Override the sort method by passing in a block in which you first perform your substitutions gsub (if necessary) and then do your comparison <=>. The sort will return the original items of the enum sorted. A solution I arrived at by reading the documentation ri Hash.sort.

Split an array into new arrays (each with a unique name) [duplicate]

This question already has answers here:
How to dynamically create a local variable?
(4 answers)
Closed 7 years ago.
I'm trying to slice an array into equal sizes (rounded down) and save each section to respective variables.
The method each_slice has worked to grab n-sized blocks. However I can't think of a way to:
iterate over the each's blocks' "sub index"
create a new array for each and give each a unique name.
letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n"]
def groups_of_five(array)
split_array = array.each_slice(5).to_a
#something like the following:
#array(n) = Array[split_array.each {|x| x}]
end
end
The output I'm hoping for:
groups_of_five(letters)
=> array1: ["a,"b","c","d","e"]
=> array2: ["f","g","h","i","j"]
=> array3: ["k","l","m","n"]
Combine each_slice with with_index and you'll have everything you need:
letters.each_slice(5).with_index(1) do |group, index|
puts "array#{index}: #{group.inspect}"
end
Output is:
array1: ["a", "b", "c", "d", "e"]
array2: ["f", "g", "h", "i", "j"]
array3: ["k", "l", "m", "n"]
It's no longer possible to set local variable dynamically in Ruby versions greather than 1.8, so if you want to assign to variables it will have to be instance variables or you could output a Hash.
The following will create instance variables:
def groups_of_five(array)
array.each_slice(5).with_index(1) do |group, index|
instance_variable_set "#array#{index}".to_sym, group
end
end
groups_of_five(letters)
puts #array1 #=> ["a", "b", "c", "d", "e"]
puts #array2 #=> ["f", "g", "h", "i", "j"]
puts #array3 #=> ["k", "l", "m", "n"]
Or this will output a Hash:
def groups_of_five(array)
hash = {}
array.each_slice(5).with_index(1) do |group, index|
hash["array#{index}".to_sym] = group
end
hash
end
hash = groups_of_five(letters)
puts hash[:array1] #=> ["a", "b", "c", "d", "e"]
puts hash[:array2] #=> ["f", "g", "h", "i", "j"]
puts hash[:array3] #=> ["k", "l", "m", "n"]
If you are looking for a hash structure to return from groups_of_five(letters), here's the solution
def groups_of_five(array)
split_array = letters.each_slice(5).to_a
split_array.reduce({}){ |i,a|
index = split_array.index(a) + 1
i["array#{index}"] = a; i
}
end
# groups_of_five(letters)
#=> {"array1"=>["a", "b", "c", "d", "e"], "array2"=>["f", "g", "h", "i", "j"], "array3"=>["k", "l", "m", "n"]}
You could do this:
def group_em(a,n)
arr = a.dup
(1..(arr.size.to_f/n).ceil).each_with_object({}) { |i,h|
h["array#{i}"] = arr.shift(n) }
end
group_em(letters,1)
#=> {"array1"=>["a"], "array2"=>["b"],...,"array14"=>["n"]}
group_em(letters,2)
#=> {"array1"=>["a", "b"], "array2"=>["c", "d"],...,"array7"=>["m", "n"]}
group_em(letters,5)
#=> {"array1"=>["a", "b", "c", "d", "e"],
# "array2"=>["f", "g", "h", "i", "j"],
# "array3"=>["k", "l", "m", "n"]}
A variant is:
def group_em(arr,n)
(1..(arr.size.to_f/n).ceil).zip(arr.each_slice(n).to_a)
.each_with_object({}) { |(i,a),h| h["array#{i}"]=>a) }
end

Resources