Skip to content

Instantly share code, notes, and snippets.

@rossta rossta/FLATTEN.md
Last active Jul 19, 2016

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

Flatten

A Ruby module to demonstrate flattening of a nested array without using Array#flatten:

Usage

Flatten.flatten([1, [2, [3]]])
=> [1, 2, 3]

Tests

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

Going Further

For an alternative usage, I also extracted the logic of this custom flatten implementation as a Ruby refinement.

module Flatten
# Returns new one-dimensional array from nested array, recursively
#
# @param array [Array] the array to be flattened
#
# @example
# Flatten.flatten([1, [2, [3]]])
# => [1, 2, 3]
#
# @return [Array]
#
def self.flatten(array)
Flattener.new(array).flatten
end
# @private class to handle custom array flattening logic without using Array#flatten
#
class Flattener
def initialize(array)
@array = array
end
def flatten
raise ArgumentError, "Argument #{@array.inspect} is not an array" unless @array.kind_of?(Array)
recursively_flatten(@array)
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'
class TestFlatten < Minitest::Test
def test_flattens_nested_array_to_one_dimension
assert_equal [1, 2, 3, 4, 5, 1, 2, 3, 4, 5], Flatten.flatten([[1,2,[3]], [4, 5], [], [1, [2, [3, 4, [5]]]]])
end
def test_empty
assert_equal [], Flatten.flatten([])
end
def test_already_flat
assert_equal [3, 3, 3], Flatten.flatten([3, 3, 3])
end
def test_does_not_modify_array
array = [1, [2]]
Flatten.flatten(array)
assert_equal [1, [2]], array
end
def test_raises_error_on_non_arrays
assert_raises(ArgumentError) { Flatten.flatten(nil) }
assert_raises(ArgumentError) { Flatten.flatten("string") }
assert_raises(ArgumentError) { Flatten.flatten(12345) }
assert_raises(ArgumentError) { Flatten.flatten({}) }
end
def test_raises_error_array_with_cycles
array = [1]
array << array
assert_raises(ArgumentError) { Flatten.flatten(array) }
array_1 = [1]
array_2 = [2]
array_1 << array_2
array_2 << array_1
assert_raises(ArgumentError) { Flatten.flatten(array) }
end
end
source "https://rubygems.org"
gem "minitest"
GEM
remote: https://rubygems.org/
specs:
minitest (5.9.0)
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.