Create an array only if necessary
Declare a simple helper that wraps non-array values in an array or leaves the original intact:
const toArray = data =>
Array.isArray(data)
? data
: [data];
Example:
const anArray = ['oneItem', 'twoItem'];
const notAnArray = 'oneItem';
const nonIterator = 300;
const oneDefault = 20;
const toArray = data =>
Array.isArray(data)
? data
: [data];
const foo = typeOrArrayOfType => console.log([...toArray(typeOrArrayOfType), oneDefault]);
foo(anArray) // works
foo(notAnArray) // works
foo(nonIterator) // works
Convert everything to new arrays
The above has a slight weakness - it returns the original array in some cases. Which means that mutations might affect it:
const toArray = data =>
Array.isArray(data)
? data
: [data];
function test(input) {
const arrOutput = toArray(input);
arrOutput.push("world");
return arrOutput;
}
const arrInput = ["hello"];
const output = test(arrInput);
console.log(output); // [ "hello", "world" ]
console.log(arrInput); // [ "hello", "world" ]
To handle this, you could copy every array uniformly using Array#concat()
- if given an array, it will produce a new array with a copy of its contents (only one level), if given non-array it will create a new array with the argument(s) as item(s):
const toArray = data =>
[].concat(data);
Example:
const anArray = ['oneItem', 'twoItem'];
const notAnArray = 'oneItem';
const nonIterator = 300;
const oneDefault = 20;
const toArray = data =>
[].concat(data);
const foo = typeOrArrayOfType => console.log([...toArray(typeOrArrayOfType), oneDefault]);
foo(anArray) // works
foo(notAnArray) // works
foo(nonIterator) // works
Example which does not have a problem with shared arrays:
const toArray = data =>
[].concat(data);
function test(input) {
const arrOutput = toArray(input);
arrOutput.push("world");
return arrOutput;
}
const arrInput = ["hello"];
const output = test(arrInput);
console.log(output); // [ "hello", "world" ]
console.log(arrInput); // [ "hello" ]
This might be even simpler to use, since it removes the need to spread items. The .concat()
method already creates a new array and accepts variable number of arguments, it can be used directly as a way to create a new array with extra items:
const anArray = ['oneItem', 'twoItem'];
const notAnArray = 'oneItem';
const nonIterator = 300;
const oneDefault = 20;
const foo = typeOrArrayOfType => console.log([].concat(typeOrArrayOfType, oneDefault));
foo(anArray) // works
foo(notAnArray) // works
foo(nonIterator) // works
Note: the @@isConcatSpreadable
well-known symbol property can affect how Array#concat()
works. When set to true
then concatinating the object will be "flatten" similar to how arrays are. This will work on any array-like. Conversely setting the property to false
will prevent .concat()
from spreading the object:
//make an object string
const str = new String('bar');
//make it spreadable
str[Symbol.isConcatSpreadable] = true;
console.log([].concat(str));
//a spreadable array-like:
const arrayLike = {
0: "h", 1: "e", 2: "l", 3: "l", 4: "o",
length: 5,
[Symbol.isConcatSpreadable]: true
};
console.log([].concat(arrayLike));
//non-spredable array:
const arr = ["x", "y", "z"];
arr[Symbol.isConcatSpreadable] = false;
console.log([].concat(arr));
.as-console-wrapper { max-height: 100% !important };