Implementing Array#flatten - arrays

I need to implement Array#flatten. This implementation removes all nested arrays:
a = [1, 2, [3, [4, 5]]]
def my_flatten(arr)
arr.reduce([]) do |result, item|
item.is_a?(Array) ? result + my_flatten(item) : result << item
end
end
my_flatten(a) #=> [1, 2, 3, 4, 5]
Prompt how to implement such behavior
a.flatten(1) #=> [1, 2, 3, [4, 5]]

Introduce a parameter to specify the max depth (defaulting to nil) and a parameter to keep track of the current depth (0 on the initial call and then incremented by 1 on each recursive call):
def my_flatten(arr, max_depth = nil, current_depth = 0)
arr.reduce([]) do |result, item|
item.is_a?(Array) && (max_depth.nil? || current_depth < max_depth) ?
result + my_flatten(item, max_depth, current_depth + 1) : result << item
end
end
You could replace the ?: with an if/else if you felt this was more readable:
def my_flatten(arr, max_depth = nil, current_depth = 0)
arr.reduce([]) do |result, item|
if item.is_a?(Array) && (max_depth.nil? || current_depth < max_depth)
result + my_flatten(item, max_depth, current_depth + 1)
else
result << item
end
end
end
This now returns the expected results:
my_flatten(a) #=> [1, 2, 3, 4, 5]
my_flatten(a, 1) #=> [1, 2, 3, [4, 5]]

I created something similar a long time back. Here is the gist link.
Code from gist:
class Array
def my_flatten(level = nil)
rb_flatten(self, [], level)
end
private
# apply recursion based on the level
# when no level provided, then produce a complete flatten array
# when level is given, then produce a flatten array flattened till that certain level
def rb_flatten(array, result, level)
array.each do |value|
if ((value.is_a? Array) && (level.nil? || (level && level > 0)))
rb_flatten(value, result, (level.nil? ? level : ((level || 0 ) - 1)))
else
result << value
end
end
return result
end
end
Hope that helps.

you can also use Proc like this.
class Array
def my_flatten(level = nil)
p = ->(arr, exp, lvl) do
arr.each { |val| Array === val && (!level || lvl < level) ? p.(val, exp, lvl+1) : exp << val }
exp
end
p.(self, [], 0)
end
end
a = [1, 2, [3, [4, 5]]]
p a.my_flatten
# => [1, 2, 3, 4, 5]
p a.my_flatten(0)
# => [1, 2, [3, [4, 5]]]
p a.my_flatten(1)
# => [1, 2, 3, [4, 5]]
p a.my_flatten(2)
# => [1, 2, 3, 4, 5]

Related

Removing duplicates as well as the corresponding values from array in Ruby

I'm using Ruby 1.9.3 and I want to remove values from an array that appear more than once. I have the following:
arr = [1,2,2,3,4,5,6,6,7,8,9]
and the result should be:
arr = [1,3,4,5,7,8,9].
What would be the simplest, shortest Ruby code to accomplish this?
As #Sergio Tulentsev mentioned combination of group_by and select will do the trick
Here you go
arr.group_by{|i| i}.select{|k, v| v.count.eql?(1)}.keys
We can achieve this by array select and count methods
arr.select { |x| arr.count(x) == 1 } #=> [1, 3, 4, 5, 7, 8, 9]
def find_duplicates(elements)
encountered = {}
# Examine all elements in the array.
elements.each do |e|
# If the element is in the hash, it is a duplicate.
if encountered[e]
#Remove the element
else
# Record that the element was encountered.
encountered[e] = 1
end
end
end
I want to remove values from an array that appear more than once.
to check element appear more than once use Array#count
to remove element conditionally use Array#delete_if
below is an example:
> arr.delete_if{|e| arr.count(e) > 1}
#=> [1, 3, 4, 5, 7, 8, 9]
Option2:
> arr.group_by{|e| e}.delete_if{|_,v| v.size > 1}.keys
#=> [1, 3, 4, 5, 7, 8, 9]
First of you need to group elements by itself (which will return key, value pair), then remove such elements which appear more than once(value), and use keys
I would be inclined to use a counting hash.
Code
def single_instances(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }.
select { |_,v| v == 1 }.
keys
end
Example
single_instances [1,2,2,3,4,5,6,6,7,8,9]
#=> [1, 3, 4, 5, 7, 8, 9]
Explanation
The steps are as follows.
arr = [1,2,2,3,4,5,6,6,7,8,9]
f = Hash.new(0)
#=> {}
f is created with the method Hash::new with an argument of zero. That means that if f does not have a key k, f[k] returns zero (and does not alter f).
enum = arr.each_with_object(f)
#=> #<Enumerator: [1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9]:each_with_object({})>
h = enum.each { |e,h| h[e] += 1 }
#=> {1=>1, 2=>2, 3=>1, 4=>1, 5=>1, 6=>2, 7=>1, 8=>1, 9=>1}
g = h.select { |_,v| v == 1 }
#=> {1=>1, 3=>1, 4=>1, 5=>1, 7=>1, 8=>1, 9=>1}
g.keys
#=> [1, 3, 4, 5, 7, 8, 9]
In calculating g, Hash#select (which returns a hash), not Enumerable#select (which returns an array), is executed. I've used an underscore for the first block variable (a key in h) to signify that it is not used in the block calculation.
Let's look more carefully at the calculation of h. The first value is generated by the enumerator enum and passed to the block, and the block variables are assigned values using a process called disambiguation or decomposition.
e, h = enum.next
#=> [1, {}]
e #=> 1
h #=> {}
so the block calculation is
h[e] += 1
#=> h[e] = h[e] + 1 => 0 + 1 => 1
h[e] on the right side of the equality (using the method Hash#[], as contrasted with Hash#[]= on the left side of the equality), returns 1 because h has no key e #=> 1.
The next two elements of enum are passed to the block and the following calculations are performed.
e, h = enum.next
#=> [2, {1=>1}]
h[e] += 1
#=> h[e] = h[2] + 1 => 0 + 1 => 1
Notice that h has been updated.
e, h = enum.next
#=> [2, {1=>1, 2=>1}]
h[e] += 1
#=> h[e] = h[e] + 1 => h[2] + 1 => 1 + 1 => 2
h #=> {1=>1, 2=>2}
This time, because h already has a key e #=> 2, the hash's default value is not used.
The remaining calculations are similar.
Use [Array#difference] instead
A simpler way is to use the method Array#difference.
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Suppose
arr = [1,2,2,3,4,2,5,6,6,7,8,9]
Note the addition of a third 2.
arr - arr.difference(arr.uniq)
# => [1, 3, 4, 5, 7, 8, 9]
The three steps are as follows.
a = arr.uniq
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = arr.difference(a)
#=> [2, 2, 6] (elements that appear more than once)
arr - b
# => [1, 3, 4, 5, 7, 8, 9]
I've proposed that Array#diffence be added to the Ruby core, but there seems to be little interest in doing so.

How do I find the first two consecutive elements in my array of numbers?

Using Ruby 2.4, I have an array of unique, ordered numbers, for example
[1, 7, 8, 12, 14, 15]
How do I find the first two elements whose difference is 1? For example, the above array the answer to that is "7" and "8".
You could use each_cons and find to get the first element from the array of pairs where the second element less the first one is equal to 1:
p [1, 7, 8, 12, 14, 15].each_cons(2).find { |a, b| b - a == 1 }
# => [7, 8]
Here are three more ways.
#1
def first_adjacent_pair(arr)
(arr.size-2).times { |i| return arr[i, 2] if arr[i+1] == arr[i].next }
nil
end
first_adjacent_pair [1, 7, 8, 12, 14, 15] #=> [7,8]
first_adjacent_pair [1, 7, 5, 12, 14, 16] #=> nil
#2
def first_adjacent_pair(arr)
enum = arr.to_enum # or arr.each
loop do
curr = enum.next
nxt = enum.peek
return [curr, nxt] if nxt == curr.next
end
nil
end
enum.peek raises a StopIteration exception when the enumerator enum has generated its last element with the preceding enum.next. The exception is handled by Kernel#loop by breaking out of the loop, after which nil is returned. See also Object#to_enum, Enumerator#next and Enumerator#peek.
#3
def first_adjacent_pair(arr)
a = [nil, arr.first]
arr.each do |n|
a.rotate!
a[1] = n
return a if a[1] == a[0] + 1
end
nil
end
See Array#rotate!.
Simple example
X = [1, 7, 8, 12, 14, 15]
X.each_with_index do |item, index|
if index < X.count - 1
if (X[index+1]-X[index] == 1)
puts item
end
end
end
Here's an alternate method provided for educational purposes:
arr = [1, 7, 8, 12, 14, 15]
arr.each_cons(2).map {|v|v.reduce(:-)}.index(-1)
One way to do this:
a.each_with_index { |e, i| break [ e, a[i.next] ] if a[i.next] == e.next }
#=> [7, 8]
Unlike chunk or each_cons this doesn't create an array of arrays. It also breaks as soon as a pair is found.
Benchmarks
require 'fruity'
arr = ((1...1000)).to_a.reverse + [1,2]
def first_adjacent_pair(arr)
idx = arr.each_index.drop(1).find { |i| (arr[i-1]-arr[i]).abs == 1 }
idx ? arr[idx-1, 2] : nil
end
def first_adjacent_pair2(arr)
enum = arr.to_enum
loop do
curr = enum.next
nxt = enum.peek
return [curr, nxt] if (curr-nxt).abs == 1
end
nil
end
compare do
iceツ { ar = arr.dup; ar.each_with_index { |e, i| break [ e, ar[i.next] ] if ar[i.next] == e.next } }
cary { ar = arr.dup; first_adjacent_pair(ar) }
cary2 { ar = arr.dup; first_adjacent_pair2(ar) }
seb { ar = arr.dup; ar.each_cons(2).find{|a,b| b-a == 1} }
end
#Running each test 64 times. Test will take about 1 second.
#cary2 is faster than cary by 3x ± 0.1
#cary is faster than iceツ by 3x ± 0.1 (results differ: [999, 998] vs [1, 2])
#iceツ is faster than seb by 30.000000000000004% ± 10.0%

Circular array slicing

I have an array with a varying number of elements 0..n elements. An example could be:
a = [0,1,2,3,4,5,6,7,8,9]
In an iterative process, I would like to move a cursor in the array and slice out a max number of elements. If I reach the "end" of the array, it should start over and pick from the beginning again:
Something like this:
4.times do |i|
a.slice(i * 3, 3)
end
# i = 0 => [0,1,2]
# i = 1 => [3,4,5]
# i = 2 => [6,7,8]
# i = 3 => [9,0,1]
# ...
However the last output i = 3 produces [9] as .slice does not do exactly what I want.
You could use cycle:
a.cycle.each_slice(3).take(4)
#=> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 0, 1]]
You could use Array#rotate, and then take the first 3 elements each time:
4.times.each { |i| a.rotate(i*3)[0..2] }
# => [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 0, 1]]
Possible solution:
4.times { |i| p a.values_at(*(i*3..i*3+2).map {|e| e % 10 }) }
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9, 0, 1]
9%10 = 9, 10%10 = 0, 11%10 = 1. So you will get the desired output.
This might break some code, so be careful.
class Array
alias_method :old_slice, :slice
def slice(o, c)
ret = old_slice(o % size, c)
if ret.size != c
ret += old_slice(0, c - ret.size)
end
ret
end
end
a = [0,1,2,3,4,5,6,7,8,9]
4.times do |i|
p a.slice(i * 3, 3)
end
As Stephan points out it would be better to give this method a different name, or it might be even better to create a CircularArray class.
class CircularArray < Array
alias_method :straight_slice, :slice
def slice(o, c)
ret = straight_slice(o % size, c)
if ret.size != c
ret += straight_slice(0, c - ret.size)
end
ret
end
end

How do I select from an array if the next element is not one plus the current element?

I want to select all elements from my array such that the element immediately after it is not one plus the current element.
I tried this:
data_col = ["46", "47", "48", "49", "50", "51", "52"]
data_col.select.with_index{|string, index| data_col[index+1] && string.to_i != data_col[index+1].to_i + 1 }
# => ["46", "47", "48", "49", "50", "51"]
Every element is selected even though I would expect none of them (except the last one) gets selected. How can I modify my statement to get what I want?
(numbers + [nil]).each_cons(2).reject { |x, y| x.next == y }.map(&:first)
Add a nil at the end. We will use that to always include the last element:
[1, 2, 3] + [nil] # => [1, 2, 3, nil]
Pair each two consecutive elements:
[1, 2, 3].each_cons(2).to_a # => [[1, 2], [2, 3]]
Remove the pairs that don't meet your criteria:
[1, 2, 5, 6, nil].each_cons(2).reject { |x, y| x.next == y }
# => [[2, 5], [6, nil]]
Get the numbers out of the pairs:
[[2, 5], [6, nil]].map(&:first) # => [2, 6]
This is one way to use Enumerable#each_cons.
arr = [1, 3, 4, 6, 7, 9]
arr.each_cons(2).
each_with_object([]) { |(x,y),a| a << x unless x.next==y } << arr[-1]
#=> [1, 4, 7, 9]
Another option is to step through an enumerator using Enumerator#next and Enumerator#peek.
enum = arr.to_enum
a = []
loop do
x = enum.next
a << x unless x.succ == enum.peek
end
a << arr[-1]
#=> [1, 4, 7, 9]
When the enumerator is at its end, Enumerator#peek generates a StopIteration exception which is handled by Kernel#loop by breaking out of the loop.
Try
data_col.select.with_index { |number_string, index| data_col[index + 1].to_i != 1 + number_string.to_i }
Or, if don't mind that you have the result elements as integer,
n_col = data_col.map(&:to_i)
n_col.select.with_index{ |n, i| n_col[i + 1] != n + 1 }
data_col = ["46", "47", "48", "49", "50", "51", "52"]
def method(arg)
results = []
i = 0
while i < arg.length
current_num = arg[i].to_i
next_num = arg[i + 1].to_i
next_num - current_num != 1 ? results << current_num : nil
i += 1
end
return results
end
p method(data_col) #[52]
This will also return just [52] like you were looking for, it uses a simple while loop, and is pretty easy to understand what's going on.

Ruby: The most elegant way to detect turning points in Array

Let's take the following Array:
[1, 4, 5, 3, 1, 4, 6, 5, 4]
It has the following turning points (when rise changes to fall, or vice versa):
5 (at index 2)
1 (at index 4)
6 (at index 6)
To make task more general:
There is an Array a = [a1, a2, ...]
There is function p(x,y) -> z, where z is Comparable
How to get all elements ai ∈ a (0 < i < a.length-1) for which p(ai-1, ai) != p(ai, ai+1)
I would like to write something like:
a.detect_edges{|prev, n| prev >= n} # => [[5,2], [1, 4], [6,6]]
What's the most elegant way to get those turning points with their respective indexes? Here's my code with which I'm not satisfied from the aesthetic point of view:
class Array
def detect_edges(&blk)
return nil if self.length < 2
prev = blk.call(self[0], self[1])
result = []
self[0..-2].each_with_index do |elem, i|
current = blk.call(elem, self[i+1])
if current != prev
result.push [elem, i]
end
prev = current
end
result
end
end
[1, 4, 5, 3, 1, 4, 6, 5, 4]
.each_cons(3).with_index(1)
.reject{|(e1, e2, e3), i| (e1 <=> e2) == (e2 <=> e3)}
.map{|(e1, e2, e3), i| [e2, i]}
# => [[5, 2], [1, 4], [6, 6]]
Look ma, no map!
a = [1, 4, 5, 3, 1, 4, 6, 5, 4]
a[1..-2].each.with_index(1).reject { |e,i| (a[i-1]<=>e) == e<=>a[i+1] }
#=> [[5, 2], [1, 4], [6, 6]]
So you basically want the elements and their indices, where the element is the local max in a 1 index range:
arr.each.with_index.select { |element, index| element == arr[index.pred..index.next].max }
# => [[5, 2], [6, 6]]
Note, you have to handle the case for the first element or if elements are equal.
EDIT: for your updated version, you just have to check if the result of <=> has changed. Note that you will again have to check the case when elements are equal:
arr.each.with_index.to_a.tap(&:pop).drop(1).select do |element, index|
(arr[index.pred] <=> element) != (element <=> arr[index.next])
end # => [[5, 2], [1, 4], [6, 6]]
I don't see reason to get more fancy than:
class Array
def detect_edges
self.collect.with_index do |e, i|
next if i == 0 || i >= size-1
yield(self[i-1],e) != yield(e,self[i+1]) ? [e, i] : nil
end.compact
end
end
Note that when patching Array one should probably use refinements.

Resources