## DEV Community is a community of 553,512 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# Introduction

As part of my TDD learning process I faced this problem and found hard to solve it following the TDD rules.
So I started this discussion --> https://dev.to/delbetu/solve-this-simple-problem-with-tdd-5e41

The TDD process I tried to follow at that point was:

1. Write a set of tests (input--->expected-output)
2. Pick the simplest test case to solve. (RED)(Write a failing spec)
3. Write the most naive code change that solves the current set of tests. (GREEN)
4. Check if DRY is needed (Refactor)
5. Go back to 2

## The change that made me reach the solution

Instead of creating a predefined set of test I created a new test conveniently for each TDD cycle.

1. Write a test case conveniently for the current state of the algorithm (RED)
2. Write the most naive code change that solves the current set of tests. (GREEN)
3. DRY if needed (Refactor)
4. Go back to 1

In the example we will understand what `conveniently` means.

# The Problem

``````flatten an array of arbitrarily nested arrays of integers into a flat array of integers. e.g. [[1,2,],4] -> [1,2,3,4].
``````

Before you continue reading try to solve it with TDD.

# Hands on!

### Cycle 1

``````def test_empty
assert_equal [], flatten([])
end

def flatten(array_tree)
[]
end
``````

### Cycle 2

``````def test_no_subarray_one_element
assert_equal , flatten()
end

def flatten(array_tree)
array_tree
end
``````

### Cycle 3

The first and only element is an array.
At this point I decided decided to tackle the `recursiveness` behavior of the algorithm.
So I conveniently chose the next set of test cases.

``````def test_with_subarray_one_element
assert_equal , flatten([])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
array_tree
else
array_tree
end
end
``````

### Cycle 4

``````def test_with_subarray_one_element
assert_equal , flatten([])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
array_tree
else
array_tree
end
end
``````

### Cycle 5

``````def test_with_subsubarray_one_element
assert_equal , flatten([[]])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
if array_tree.is_a?(Array)
array_tree
else
array_tree
end
else
array_tree
end
end
``````

### Cycle 6

At this point you can recognize a recursive pattern.

``````def test_with_subsubarray_one_element
assert_equal , flatten([[[]]])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
if array_tree.is_a?(Array)
if array_tree.is_a?(Array)
array_tree
else
array_tree
end
else
array_tree
end
else
array_tree
end
end

# Refactor
def flatten(array_tree)
if array_tree.is_a?(Array)
flatten(array_tree)
else
array_tree
end
end
``````

### Cycle 7

At this point I realized of two things about the generalization of the algorithm.
These two points will make me pick the test for generalize that part of the behavior in future tests.

``````def test_when_first_and_second_are_subarray
assert_equal [1,2], flatten([, ])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
result = flatten(array_tree) # I will need this for each sub_array
if array_tree.is_a?(Array)
result = result + flatten(array_tree)
end
result
else
array_tree # This should return only the numbers not the subarrays
end
end
``````

### Cycle 8

At this point I realized how to combine the recursive calls, so I decided to refactor.

``````def test_when_all_are_subarrays
assert_equal [1, 2, 3], flatten([, , ])
end

def flatten(array_tree)
if array_tree.is_a?(Array)
result = flatten(array_tree) # call flatten for each sub_array and concatenate results
if array_tree.is_a?(Array)
result = result + flatten(array_tree)
if array_tree.is_a?(Array)
result = result + flatten(array_tree)
end
end

result
else
array_tree # elements without subarrays
end
end

# Refactor --> Generalize the ifs
def flatten(array_tree)
if array_tree.is_a?(Array)
sub_arrays = array_tree.select {|x| x.is_a?(Array)}
sub_arrays.reduce([]) do |result, sub_array|
result + flatten(sub_array)
end
else
array_tree
end
end
``````

### Cycle 9

Tackling one of the pending behaviors found in cycle 5.
The else branch should return only the numbers not the subarrays
If the first element of the input is a number but the second is a subarray then the else branch would fail.
So the next test could be:
`[1, ] --> [1, 2]`

``````def test_when_first_is_number_and_second_is_subarray
assert_equal [1, 2], flatten([1, ])
end

def flatten(array_tree)
result = array_tree.reject {|x| x.is_a?(Array)} # elements without subarrays
sub_arrays = array_tree.select {|x| x.is_a?(Array)}
result = result + sub_arrays.reduce([]) do |accumulator, sub_array|
accumulator + flatten(sub_array)
end
end
``````

### Cycle 10

At this point I think I found the generalized solution so the next test is a complicated case.

``````def test_complicated_case
assert_same_elements [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], flatten([[[1, 2, 3]], [4, [5, 6]], 7, [[[], 9]], 0])
end

def flatten(array_tree)
result = array_tree.reject {|x| x.is_a?(Array)} # elements without subarrays
sub_arrays = array_tree.select {|x| x.is_a?(Array)}
result = result + sub_arrays.reduce([]) do |accumulator, sub_array|
accumulator + flatten(sub_array)
end
end

private

def assert_same_elements(array1, array2)
assert array1 - array2 == []
end

`````` 