How do I spread a list out with spacers? - ruby

I want to evenly spread out a list, like [1,2,3,4], into a different list, like [1, nil, nil, 2, nil, nil, 3, nil, 4, nil], based on how big I want the new list to be.
So
[1, 2, 3, 4] to a list with a size of 9 would be: [1, nil, nil, 2, nil, 3, nil, 4, nil]
And [1,2,3,4] to a list with a size of 4 would be: [1,2,3,4]
I will need to spread "uneven" spacers, which should go from left to right - look at my first example.
I answered my own question below.

I decided to make a gem for this: gem install CreamCheese
After installing and requiring the gem (require 'CreamCheese'), you can spread out a list with the knife.
require 'CreamCheese'
CreamCheese::Knife.spread [1,2,3,4], 9 #=> [1, nil, nil, 2, nil, 3, nil, 4, nil]
CreamCheese::Knife.spread [1,2,3,4], 4 #=> [1,2,3,4]

I wouldn't go as far as using a gem for this one, as it can be solved in a couple of lines of Ruby. Unfortunately, you don't specify precisely how you want to handle cases where the spread isn't even ("even" being when each item is followed by the same number of spacers).
In the simple case of an even spread:
# The general case
spread_array = array.flat_map {|el| [el] + [spacer] * (spread / array.size - 1)
# An example
[1, 3].flat_map {|el| [el] + [nil] * 2 } #=> [1, nil, nil, 3, nil, nil]
If you wish to add the leftover spacing after the leftmost elements, you can use this:
# Extra spread goes on the leftmost items
spread_array = array.flat_map.with_index do |el, i|
[el] + [spacer] * (spread / array.size - (i < spread % array.size ? 0 : 1))
end

Related

error when searching through 2d array ruby

I have the following grids (connect four)
grid1 = [
[nil, nil, nil],
[1, nil, nil],
[1, nil, nil],
[1, nil, nil]
]
grid2 = [
[nil, nil, nil],
[nil, nil, 1],
[nil, nil, 1],
[nil, nil, 1]
]
grid3 = [
[nil, nil, nil],
[nil, nil, nil],
[nil, nil, nil],
[1, 1, 1]
]
and this is the method I created to find three 1's in a vertical row and return the next available slot above
def searchArray(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
if array[yi][xi] != nil && array[yi][xi] == array[yi+1][xi] && array[yi][xi] == array[yi+2][xi]
return v = [yi-1, xi]
end
end
end
end
searchArray(grid2)
When I call the method on grid1, and grid 2 it works great but when I call it on Grid 3 the grid where the 1's are placed on the bottom row I get this error
undefined method `[]' for nil:NilClass
(repl):28:in `block (2 levels) in searchArray'
(repl):27:in `each'
(repl):27:in `each_with_index'
(repl):27:in `block in searchArray'
(repl):26:in `each'
(repl):26:in `each_with_index'
(repl):26:in `searchArray'
(repl):36:in `<main>'
Not sure what's going on
Thanks
You can solve a lot of problems here by simplifying this code using dig:
def search_array(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
stack = (0..2).map { |o| array.dig(yi + o, xi) }
if (stack == [ 1, 1, 1 ])
return [ yi - 1, xi ]
end
end
end
end
Where dig can poke around and not cause exceptions if it misses the end of the array. Here map is used to quickly pull out an N high stack. You can do 1..2 or 0..4 or whatever is necessary.
Let's take a look at your code, simplified slightly1:
def search_array(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
return [yi-1, xi] if x != nil && x == array[yi+1][xi] && x == array[yi+2][xi]
end
end
end
You go one row at a time, then for each element in that row, check if that element is not nil and if so, determine whether the two elements below it have the same non-nil value. If you reach the penultimate (next-to-last) row, yi = array.size - 2, you will compare x with array[yi+2][xi], which equals array[array.size][xi], which in turn equals nil[xi]. However, nil has no method [] so an undefined method exception is raised. Pay close attention to those error messages; often, as here, they guide you to the error.
Another problem is that if you found 1's in the first three rows of a column j you would return the index [-1, j], -1 being 0-1. You don't want that either.
I understand that you also wish to determine if dropping a coin in a column results in four-in-a-row horizontally. You could check both vertically and horizontally as follows.
def search_array(arr)
arr.first.each_index do |j|
r = arr.each_index.find { |i| arr[i][j] == 1 }
next if r == 0
r = r.nil? ? arr.size-1 : r-1
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j)
end
nil
end
def below?(arr,r,j)
r < arr.size-3 && (1..3).all? { |i| arr[r+i][j] == 1 }
end
def right?(arr,r,j)
j < arr.first.size-3 && (1..3).all? { |i| arr[r][j+i] == 1 }
end
def left?(arr,r,j)
j >= 3 && (1..3).all? { |i| arr[r][j-i] == 1 }
end
grid4 = [
[nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil],
[nil, nil, 1, nil, nil],
[nil, nil, 1, 1, 1],
[ 1, 1, 1, nil, 1]
]
grid5 = [
[nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil],
[nil, nil, 1, nil, nil],
[nil, 1, 1, nil, nil],
[nil, 1, 1, nil, 1]
]
search_array grid1 #=> [0, 0] (vertical)
search_array grid2 #=> [0, 2] (vertical)
search_array grid3 #=> nil
search_array grid4 #=> [3, 1] (horizontal)
search_array grid5 #=> [1, 2] (vertical)
Note that if you wish to also check for four-in-a-row diagonnal you could change:
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j)
to
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j) ||
top_left_to_bottom_right?(arr,r,j) || bottom_left_to_top_right?(arr,r,j)
and add the additional methods top_left_to_bottom_right? and bottom_left_to_top_right?.
1. I changed the name of your method to search_array because Ruby has a convention to use snake case for the naming of variables and methods. You don't have to adopt that convention but 99%+ of Rubiests do.
I could suggest a slight different approach, this is not a complete solution, just a start. It should also help to catch the four.
First map the not nil indexes of the grid, let's consider grid3:
mapping = grid3.flat_map.with_index{ |y, yi| y.map.with_index { |x, xi| [xi, yi] if x }.compact }
#=> [[0, 3], [1, 3], [2, 3]]
Then group by first and second element to get the columns and rows:
cols = mapping.group_by(&:first) #=> {0=>[[0, 3]], 1=>[[1, 3]], 2=>[[2, 3]]}
rows = mapping.group_by(&:last) #=> {3=>[[0, 3], [1, 3], [2, 3]]}
Now, if you want to look for three elements in a row or in a column:
cols.keep_if { |_,v| v.size == 3 } #=> {}
rows.keep_if { |_,v| v.size == 3 } #=> {3=>[[0, 3], [1, 3], [2, 3]]}
The first line says there are no columns with three element aligned.
The second line says that row with index 3 has three elements aligned and indexes are [[0, 3], [1, 3], [2, 3]].
Next step it to check that there are no gaps amongst elements. For example in a 4x4 grid you could get also [[0, 3], [1, 3], [3, 3]] which are three elements, but there is a gap in [2, 3],

sum of corresponding elements of array including nil

How can I sum in Ruby
[1, 2, nil, 4]
with
[nil, 2, nil, 4]
and have
[1, 4, nil, 8]
?
For example as follows:
a = [1, 2, nil, 4]
b = [nil, 2, nil, 4]
a.map.with_index {|v,i| (v || b[i]) && v.to_i + b[i].to_i }
More estetic way is:
a.zip(b).map {|v| v.compact.reduce(:+) }
Explanation: Here #zip just reconstructs the array with other passed value-by-value, so in 1st row will consist of 1st elements of each array, 2nd of 2nds, etc. Then #compact, and #reduce is applied of each of rows 1st, 2nd, etc. #compact just removes nil value, so they do not included in result. #reduce then construct a sum for each row, via :+ operator between sum (by default it is nil), and value, so in output it resulted in the sum of values, of nil in case empty row.
If we'll get the more general approach, let's sum rows of the matrix.
m = [ [ 1, 2, nil, 4],
[nil, 2, nil, 4] ]
m.shift.zip(*m).map {|v| v.compact.reduce(:+) }
a.zip(b).map {|x, y| x.nil? ? (y.nil? ? nil : y) : (y.nil? ? x : x + y)}
# => [1, 4, nil, 8]
Here's a way that makes use of the fact that nil.to_i => O:
a.zip(b).map { |e,f| [e,f]==[nil,nil] ? nil : e.to_i + f.to_i }

Ruby check array, return the indexes, which data is exist

How can I check if there's a data that not nil in an array, and then return the index of that data?
Example:
myary = [nil, nil, 300, nil, nil] # <= index 2 is 300
now is there a method to get the value 2? As we know the index 2 is 300 and not nil.
I need to get the index not the value. And moreover there probably will ot only one element that is not nil, perhaps the array could be like this
myotherary = [nil, nil, 300, 400, nil] # <= index 2,3 = 300,400
now for this I need to get 2 and 3 value, is this posibble?
Okay thank you very much, I appreciate all answer.
P.S : Please no flaming, if you don't want to help then just leave, I have spent some time to solve this matter and not succeed. I'm not going to ask here if I can solve it by myself. I had enough of them who not helping, instead asking "what method have you tried?" or write something else that actually not helping but harrasing.
You can use map.with_index:
myary.map.with_index { |v, i| i if v }.compact
# => [2]
myotherary.map.with_index { |v, i| i if v }.compact
# => [2, 3]
I would be inclined to use Enumerable#select in part because it reads well; the word "select" describes what you want to do.
Code
For just the indices:
def indices_only(arr)
arr.size.times.select { |i| arr[i] }
end
If it would be more useful to return both non-nil values and corresponding indices:
def values_and_indices(arr)
arr.each_with_index.select(&:first)
end
Examples
arr1 = [nil, nil, 300, nil, nil]
arr2 = [nil, nil, 300, 400, nil]
indices_only(arr1) #=> [2]
indices_only(arr2) #=> [2, 3]
values_and_indices(arr1) #=> [[300, 2]]
values_and_indices(arr2) #=> [[300, 2], [400, 3]]

Making map! enumerator do what I want

Having an array
a = 1, 2, 3, 4
And an enumerator:
e = a.map!
Then, by calling e.next repeatedly, array a gets nicely destroyed:
e.next #=> 1
a #=> [1, 2, 3, 4]
e.next #=> 2
a #=> [nil, 2, 3, 4]
e.next #=> 3
a #=> [nil, nil, 3, 4]
That's so hilarious! But when I try
e.next { |x| 2 * x } # => 4
I get
a #=> [nil, nil, nil, 4]
instead of desired
a #=> [nil, nil, nil, 8]
What am I misunderstanding? How to make a.map! do what I want with the elements?
My problem is, that I do not fully understand enumerators. With the previous code in place, for example, enumerator e constitutes a backdoor to a:
e.each { 42 }
a #=> [42, 42, 42, 42]
I would like to know, how to do this gradually, with values other than nil. (I can gradually fill it with nils using e.rewind and e.next several times, as I shown before.
To make map! behave as you want, you need the Enumerator#feed method, consider this
ary = *1..4
enum = ary.map!
# the `loop` method handles `StopIteration` for us
loop do
x = enum.next
enum.feed(x * 2)
end
ary
# => [2, 4, 6, 8]
From reference it seems that Enumerator#next doesn't accept a block, so that doesn't have effect of your next call. If you just want to in-place double the last element while clearing all other, do something like, consider straight approach (like a = a[0..-2].map!{|x| nil} + [a.last*2], maybe more elegant). Anyway, could you please provide us with a more detailed usecase to make sure you are doing what you really need?
a.map! accepts a block, but returns an enumerator if no block is supplied. Enumerator#next does not accept a block.
You want to use this to accomplish your goal:
a.map! {|x| x * 2}
if you want to multiply all elements in the array by 2.
For info on next, check out http://ruby-doc.org/core-2.0/Enumerator.html#method-i-next
If you want the output to be exactly [nil, nil, nil, 8] you could do something like:
func = lambda { |x|
unless x == 4
nil
else
x * 2
end
}
a.map!(&func) #> [nil, nil, nil, 8]

Remove adjacent identical elements in a Ruby Array?

Ruby 1.8.6
I have an array containing numerical values. I want to reduce it such that sequences of the same value are reduced to a single instance of that value.
So I want
a = [1, 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3]
to reduce to
[1, 2, 3, 2, 3]
As you can see, Array#uniq won't work in this case.
I have the following, which works:
(a.size - 1).downto(1) { |i| a[i] = nil if a[i - 1] == a[i] }
Can anyone come up with something less ugly?
For the simplest, leanest solution, you could use the method Enumerable#chunk:
a.chunk(&:itself).map(&:first)
The itself method is Ruby 2.2+. Use {|n| n} if you are stuck in an older Ruby, or my backports gems.
It was introduced in Ruby 1.9.2. If you're unlucky enough to be using older rubies, you could use my backports gem and require 'backports/1.9.2/enumerable/chunk'.
a.inject([]){|acc,i| acc.last == i ? acc : acc << i }
I can think only of this
a.each_with_index{|item,i| a[i] = nil if a[i] == a[i+1] }.compact
but it is more or less the same.
Unless you are very concerned with the speed that block will calculate at, I would suggest you simply add this line to the end of your block to get the desired output:
a.compact!
That will just remove all the nil elements you introduced to the array earlier (the would-be duplicates), forming your desired output: [1, 2, 3, 2, 3]
If you want another algorithm, here is something far uglier than yours. :-)
require "pp"
a = [1, 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3]
i = 0
while i < a.size do
e = a[i]
j = i
begin
j += 1
end while e == a[j]
for k in i+1..j-1 do
a[k] = nil
end
i = j
end
pp a
a.compact!
pp a
Gives you the output:
[1, nil, nil, 2, nil, 3, nil, nil, nil, 2, nil, nil, 3, nil, nil]
[1, 2, 3, 2, 3]
In my opinion, your code is fine. Just add the a.compact! call and you are sorted.
another solution:
acc = [a[0]]
a.each_cons(2) {|x,y| acc << y if x != y}
or
a.each_cons(2).inject([a[0]]) {|acc, (x,y)| x == y ? acc : acc << y}
If the numbers are all single digits 0-9: a.join.squeeze('0-9').each_char.to_a should work.

Resources