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

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

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)

Zip all array values of hash

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]]

Regexp ignores some letters

I'm trying to solve Chasing Subs problem. I'm trying to generate that regex according to the input data. The goal is go get all substrings (including overlapped ones) with all unique letters.
I'm trying to use regexp like this:
regexp = /(?=(?<gs>.)(?<gu>[^\k<gs>])(?<gb>[^\k<gs>\k<gu>])(?<gm>[^\k<gs>\k<gu>\k<gb>])(?<ga>[^\k<gs>\k<gu>\k<gb>\k<gm>])(?<gr>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>])(?<gi>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>])(?<gn>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>\k<gi>])(?<ge>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>\k<gi>\k<gn>]))/
"archipelago".scan(regexp) #=> []
"archipelbgo".scan(regexp) #=> []
"brchipelbgo".scan(regexp) #=> []
"zrchipelzgo".scan(regexp) #=> [["z", "r", "c", "h", "i", "p", "e", "l", "z"]]
Why does it behave like this? Why can't it find anything with "b" and "a"? And why does it return only one (incorrect) result with "z"? What am I doing wrong?
I don't think a regular expression is the correct tool for this problem. We could do the following, however.
def substrings(str)
arr = str.chars
(1..str.size).each_with_object([]) { |n,a|
arr.each_cons(n) { |b| a << b.join if b == b.uniq } }
end
substrings("archipelago")
#=> ["a", "r", "c", "h", "i", "p", "e", "l", "a", "g", "o", "ar", "rc", "ch", "hi",
# "ip", "pe", "el", "la", "ag", "go", "arc", "rch", "chi", "hip", "ipe", "pel",
# "ela", "lag", "ago", "arch", "rchi", "chip", "hipe", "ipel", "pela", "elag",
# "lago", "archi", "rchip", "chipe", "hipel", "ipela", "pelag", "elago", "archip",
# "rchipe", "chipel", "hipela", "ipelag", "pelago", "archipe", "rchipel", "chipela",
# "hipelag", "ipelago", "archipel", "rchipela", "chipelag", "hipelago", "rchipelag",
# "chipelago", "rchipelago"]

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 ""
#=> [[]]

Test if elements of one array are included in another

I have the following arrays:
passing_grades = ["A", "B", "C", "D"]
student2434 = ["F", "A", "C", "C", "B"]
and I need to verify that all elements in the student array are included in the passing_grades array. In the scenario above, student2434 would return false. But this student:
student777 = ["C", "A", "C", "C", "B"]
would return true. I tried something like:
if student777.include? passing_grades then return true else return false end
without success. Any help is appreciated.
PASSING_GRADES = ["A", "B", "C", "D"]
def passed?(grades)
(grades - PASSING_GRADES).empty?
end
similar to what CDub had but without bug. more readable in my opinion
You could have a method that does the difference of the arrays, and if any results are present, they didn't pass:
PASSING_GRADES = ["A", "B", "C", "D"]
def passed?(grades)
grades.all? {|grade| PASSING_GRADES.include?(grade)}
end
Example:
1.9.3-p484 :117 > student777 = ["C", "A", "C", "C", "B"]
=> ["C", "A", "C", "C", "B"]
1.9.3-p484 :118 > passed?(student777)
=> true
1.9.3-p484 :118 > passed?(student2434)
=> false

Resources