Skip to content

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
You can’t perform that action at this time.