Instantly share code, notes, and snippets.

Embed
What would you like to do?
Custom Ruby refinement to demonstrate flattening of an Array with using Array#flatten

FlattenRefinement

A Ruby refinement to demonstrate flattening of a nested array without using Array#flatten. Flattening logic is identical to my original implementation done as a Ruby module.

Exposes Array#custom_flatten when used as a refinement; one advantage to using a refinement is not having to check for non-array argument types. I'd be less inclined to use refinements in a production app given they haven't gained a lot of traction in the Ruby community, but I believe they're good for experimentation.

Usage

using FlattenRefinement

[1, [2, [3]]].custom_flatten
=> [1, 2, 3]

Tests

Code was developed using Ruby 2.3.1. To run tests, run bundle install and bundle exec ruby flatten_refinement_test.rb

module FlattenRefinement
refine Array do
# Returns new one-dimensional array from nested array, recursively
#
# @example
# using FlattenRefinement
#
# [1, [2, [3]]].custom_flatten
# => [1, 2, 3]
#
# @return [Array]
#
def custom_flatten
recursively_flatten(self)
end
private
# @private handles array flattening recursively while detecting cycles
#
def recursively_flatten(array, visited = {})
array.each_with_object([]) do |element, new_array|
case element
when Array
object_id = element.object_id
raise ArgumentError, "Cannot flatten array with cycles" if visited[object_id]
visited[object_id] = true
new_array.concat recursively_flatten(element, visited)
else
new_array << element
end
end
end
end
end
require 'minitest/autorun'
require 'minitest/pride'
require_relative './flatten_refinement'
class TestFlattenRefinement < Minitest::Test
using FlattenRefinement
def test_flattens_nested_array_to_one_dimension
assert_equal [1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [[1,2,[3]], [4, 5], [], [1, [2, [3, 4, [5]]]]].custom_flatten
end
def test_empty
assert_equal [], [].custom_flatten
end
def test_already_flat
assert_equal [3, 3, 3], [3, 3, 3].custom_flatten
end
def test_does_not_modify_array
array = [1, [2]]
array.custom_flatten
assert_equal [1, [2]], array
end
def test_raises_error_array_with_cycles
array = [1]
array << array
assert_raises(ArgumentError) { array.custom_flatten }
array_1 = [1]
array_2 = [2]
array_1 << array_2
array_2 << array_1
assert_raises(ArgumentError) { array.custom_flatten }
end
end
source "https://rubygems.org"
gem "minitest"
GEM
remote: https://rubygems.org/
specs:
minitest (5.8.4)
PLATFORMS
ruby
DEPENDENCIES
minitest
BUNDLED WITH
1.12.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment