Skip to content

Instantly share code, notes, and snippets.

@anri-asaturov
Last active January 8, 2018 22:32
Show Gist options
  • Save anri-asaturov/c749c3dba70aee77ad52b0d7d3e66ce6 to your computer and use it in GitHub Desktop.
Save anri-asaturov/c749c3dba70aee77ad52b0d7d3e66ce6 to your computer and use it in GitHub Desktop.
Flatten array
// ------------- MODULE -------------------
/**
* Flattens array containing nested arrays into a one-level array.
* @param {Array} arr - array to flatten
* @returns {Array} flat array, always a new instance
*/
function flattenArray(arr) {
if (!Array.isArray(arr)) throw new Error('First argument should be Array.');
// result accumulator
const ret = [];
// starts recursive call chain
iterate(ret, arr);
return ret;
}
/**
* Helper function to process one array and make recursive calls for nested arrays.
* @param {Array} accumulator - result accumulator
* @param {Array} arrLevel - one array level to process
*/
function iterate(accumulator, arrLevel) {
// not verifying arguments since it's an internal module function
arrLevel.forEach(item => {
if (Array.isArray(item)) {
// process nested array
iterate(accumulator, item);
} else {
accumulator.push(item);
}
});
}
// module.exports = { flattenArray };
// -------------- TESTING -------------------------------------------
// I do know and use several test frameworks and assertion libraries
// in my everyday work (recently mocha, chai and cucumberjs)
// but I assumed you will want to paste this gist to console
// and see if it works, so, here's a vanilla test runner :D
/**
* Test helper, performs shallow comparison of 2 arrays
* @param {Array} arr1
* @param {Array} arr2
* @returns {bool} true - if length and elements of one array are strictly equal to elements of the other
*/
function shallowCompareArrays(arr1, arr2) {
if (!Array.isArray(arr1)
|| !Array.isArray(arr2)
|| arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
return true;
}
/**
* @callback testCaseCode
* @returns {Array} actual value
*/
/**
* Test helper to execute one test case.
* @param {string} name - test case name
* @param {testCaseCode} testFn - test case code
* @param {Array} expected - expected result,
* @param {bool} [shouldThrow=false] - pass 'true' if test case is expected to throw
*/
function runTestCase(name, testFn, expected, shouldThrow = false) {
console.log(`Test case: ${name}...`);
let result = true;
try {
const actual = testFn();
result = shallowCompareArrays(actual, expected);
console.assert(
result,
'Expected result did not match actual',
'expected:', expected,
'actual:', actual
);
} catch (err) {
if (!shouldThrow) {
console.error(err);
return;
}
}
if (result) console.log('...success!');
}
// [
// case name : string,
// case code : function,
// expected result : array,
// should throw : [bool]
// ]
const testCases = [
[
'undefined argument',
() => flattenArray(),
null,
true
], [
'null argument',
() => flattenArray(null),
null,
true
], [
'Number argument',
() => flattenArray(42),
null,
true
], [
'String argument',
() => flattenArray('[1,2,3]'),
null,
true
], [
'Empty array',
() => flattenArray([]),
[]
], [
'Single element',
() => flattenArray([[[1]]]),
[1]
], [
'Flat array',
() => flattenArray([1, 2, 3, 4, 5]),
[1, 2, 3, 4, 5]
], [
'2 levels - array start',
() => flattenArray([[1, 2, 3, 4], 5]),
[1, 2, 3, 4, 5]
], [
'2 levels - array middle',
() => flattenArray([1, [2, 3], 4, 5]),
[1, 2, 3, 4, 5]
], [
'2 levels - array end',
() => flattenArray([1, 2, 3, [4, 5]]),
[1, 2, 3, 4, 5]
], [
'2 levels - full array',
() => flattenArray([[1, 2, 3, 4, 5]]),
[1, 2, 3, 4, 5]
], [
'3 levels',
() => flattenArray([[1, [2]], 3, [4, 5]]),
[1, 2, 3, 4, 5]
]
];
function vanillaTestRunner() {
testCases.forEach(testCase => runTestCase(...testCase));
}
//------
vanillaTestRunner();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment