Convert Array to Array of Arrays - arrays

I have an array like this one:
array = ['john', 'jennifer', 'kristen', 'ted']
I would like to convert it to an array of arrays of k elements.
For example, if k = 2 the result should be:
[['john', 'jennifer'], ['kristen', 'ted']]
Is it possible to do so in one line?

each_slice might help:
array.each_slice(2).to_a
#=> [["john", "jennifer"], ["kristen", "ted"]]

If you want to create two arrays from one with a predicate (an expression which evaluates to either true or false), I would recommend partition
array.partition{ |name| name[0] == 'j' }
#=> [["john", "jennifer"], ["kristen", "ted"]]

Related

returning a modified array after comparing in ruby

I have two arrays:
arr1 = [10,20,7]
arr2=[]
On the first array I am performing a division calculation similar to this:
arr1.each do |i|
res = i.to_f/2.0
arr2.push(res.round)
end
This will return arr2 = [5,10,4]
From the output array again I want to compare with the first array i.e [10,20,7]
If the output array arr2 contains value from any of the value from arr1
I want to replace that value with value/2.0
I am able to do a comparison like below:
arr2.any?{|x| arr1.include?(x)}
But I want to replace that value after comparing. How can I do that?
As any? only returns a boolean by evaluating the condition in the block, it doesn't allow you to do any modification to the receiver.
You can do that by using map and return a new object, where the values present in arr2 and in arr1 are divided by 2.0:
arr2.map do |x|
match = arr1.find { |y| x == y }
match ? match / 2.0 : x
end
# [5, 5.0, 4]
find allows you to look for elements in the receiver that match the condition in the block and return the first of them if exists, otherwise nil.

How do I figure out if my array has two consecutive elements that match a regular expression?

I'm using Rails 5 and Ruby 2.4. If I have an array of strings, how do I figure out if there are two consecutive strings that match a regular expression?
For instance, I have:
["1234", "aa", "cc33", "44"]
I want to see if there are two consecutive elements that begin with letters (in the above case, that condition is true, "aa" and "cc33"). But in the below case it would be false:
["bb", "55", "mm", "77"]
This
my_arr.select { |str| str =~ /^\p{L}/ }
tells me how many strings begin with letters, but it doesn't tell me if I have two consecutive elements that begin with letters.
How do I figure that out?
Using your same regex, you could do this:
my_arr.each_cons(2).any? { |pair| pair.all? { |elem| elem =~ /^\p{L}/ } }
Check this snippet.
I understand you wish to determine if two consecutive elements of an array match a given regular expression. To do that efficiently we would like check as few elements (strings) as possible for matches. In particular, we don't want to necessarily check all elements of the array or check individual elements twice. Here is one way to do that.
arr = ["1234", "aa", "cc33", "44"]
r = /\A[[:alpha:]]{2}/
a = [false, false]
arr.any? do |s|
a[0] = s.match?(r)
a.rotate! == [true, true]
end
#=> true
String#match? made its debut in Ruby v2.4. For earlier versions one could write !!(s =~ r). The inner exclamation mark convert a truthy value to false and a falsy value to true; the outer exclamation mark flips that result from true to false and vice-versa.

Why is it not possible to fill an array using an each do loop in Ruby?

If I use an each do loop to fill an array, it will leave the array as it is (in this case it will be a nil array of size 4)
array = Array.new(4)
array.each do |i|
i = 5
end
I understand that I can initialize an array with my desired value using array = Array.new(4) {desired value} but there are situations in which I'm choosing between different values and I am trying to understand how the each do loop work exactly.
The current way I'm doing it is the following which fills in the array with my desired value
array = Array.new(4)
array.each_with_index do |val, i|
array[i] = 5
end
Solution
You need :
array = Array.new(4) do |i|
5
# or some logic depending on i (the index between 0 and 3)
end
Your code
array = Array.new(4)
array is now an Array with 4 elements (nil each time).
array.each iterates over those 4 elements (still nil), and sets i as block-local variable equal to nil.
Inside this block, you override i with 5, but you don't do anything with it. During the next iteration, i is set back to nil, and to 5, and so on...
You don't change the original array, you only change local variables that have been set equal to the array elements.
The difference is that
i = 5
is an assignment. It assigns the value 5 to the variable i.
In Ruby, assignments only affect the local scope, they don't change the variable in the caller's scope:
def change(i)
i = 5 # <- doesn't work as you might expect
end
x = nil
change(x)
x #=> nil
It is therefore impossible to replace an array element with another object by assigning to a variable.
On the other hand,
array[i] = 5
is not an assignment, but a disguised method invocation. It's equivalent to:
array.[]=(i, 5)
or
array.public_send(:[]=, i, 5)
It asks the array to set the element at index i to 5.

Comparing a hashes value against array in order

If I have the following hash and array
hash = {'i' => 'i', 'av' => 'av', 'deviceName' => 'Genymotionvbox86p'}
array = ['i', 'av', 'Genymotionvbox86p']
How could I compare that each item in the array matches the hashes value in the same order
So far I have
array.each do |value|
hash.each do |k, v|
expect(v).to eq(value), "expected #{k} to equal #{v}, instead got #{value}"
end
end
This is failing as I get
expected av to equal av, instead got i (RSpec::Expectations::ExpectationNotMetError)
I'm not quite there yet and imagine that a loop within a loop is not the best thing to do either?
I would like to know how to efficiently approach this.
The reason this fails is because you compare every array value with every hash value. To solve this, you can take advantage of the fact that two arrays arrays are equal if all their values in order are equal:
expect(array).to eq hash.values
If you would really want to compare item-by-item, you rightfully noticed that a loop within a loop is not the way to go. You need a single loop to iterate both structures.
For that, you can, for example, use zip, to combine hash and array:
hash.zip(array).each do |(hash_key, hash_value), array_item|
expect(hash_value).to eq array_item
end
or you can resort to using an index:
hash.each_with_index do |(k, v), i|
expect(v).to eq array[i]
end
How could I compare that each item in the array matches the hashes
value in the same order
how about this?
> array == hash.values
#=> true
> array = ["i", "Genymotionvbox86p", "av"] # change the order
> array == hash.values
#=> false

Ruby method to sum all values in a multidimensional array

I am trying to sum the elements of an array. WITHOUT using flatten. I have tried using the following:
def multi_array_sum(arr)
sum = 0
arr.each do |row|
row.each do |column|
sum += column
end
end
return sum
end
but unfortunately, it is not working. I am not sure how to iterate though a multidimensional array considering that I cannot use each if the first element in the array is not another array itself, for example, array = [[1, [1, 2], [3, 4, 5]].
If all elements are numeric or arrays, and you want to sum them all:
array.flatten.inject(:+)
Just use standard array functions and then enumerable.
array.flatten.reduce(:+)
If you are 100% sure that you cannot use flatten then you can use:
array.map { |a| a.reduce(:+) }.reduce(:+)
So if you absolutely can't use flatten (which is a bit weird), you can use a simple recursive method:
def multi_array_sum(arr)
case arr
when Fixnum
arr
when Array
arr.reduce(0) { |agg, sub_arr| agg + multi_array_sum(sub_arr) }
end
end
If each inner array is the same size and contains only numbers, you could use the method Matrix#[] to convert the array to a matrix, Matrix#each (without a block) to create an enumerator to generate the elements of the matrix, and Enumerable#reduce (aka inject) to compute the sum of all elements of the matrix.
require 'matrix'
def sum_all(arr)
Matrix[*arr].each.reduce(:+)
end
sum_all [[1,2],[3,4]] #=> 10

Resources