4

I was working on a problem for flattening arrays. I came across something really strange and can't seem to find an answer online about it.

Why does

[] + [1,2] = '1,2'

I can't seem to wrap my head around why adding an empty array to a populated one results in a string with the contents of the populated array.

What is happening behind the scenes that causes this?

Example from my code:

arr = [1, [2], [3, 4]];
arr.reduce(flatten, []); // [1, 2, 3, 4]

function flatten(a, b) {
    return a.concat(b);
}

As far as I understand, reduce will set '[]' as the 'initial value' and thus for each element in the original array it will concatenate it with an empty array thus "flattening" the array.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vampiire
  • 1,111
  • 2
  • 15
  • 27
  • 1
    JavaScript is not Python, the + operator does not concatenate lists. The arrays are being coerced to a string. Try `[1,2].toString();`. – Jared Smith Apr 29 '17 at 00:37
  • `+` is both the arithmetic addition and _string_ concatenation operator. So the arrays are converted to strings automatically to match the argument type of the operator. Check out [Array.prototype.concat](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/concat?v=control) for array concatenation. – traktor Apr 29 '17 at 00:39

3 Answers3

5

When you use + between objects (arrays are objects), JavaScript calls toString() and/or valueOf() internally.

var coercion1 = {} + {foo: 'Foo', bar: 'Bar'},
    coercion2 = [] + ['foo', 'bar'];

console.log(coercion1);
console.log(coercion2);

This is the same thing as:

var coercion1 = "".concat({}.toString(), {foo: 'Foo', bar: 'Bar'}.toString()),
    coercion2 = "".concat([].toString(), ['foo', 'bar'].toString());

console.log(coercion1);
console.log(coercion2);

Which is, again, the same thing as:

var coercion1 = "".concat({}.valueOf(), {foo: 'Foo', bar: 'Bar'}.valueOf()),
    coercion2 = "".concat([].valueOf(), ['foo', 'bar'].valueOf());

console.log(coercion1);
console.log(coercion2);

To convert an object to a string, JavaScript takes these steps:

  • If the object has a toString() method, JavaScript calls it. If it returns a primitive value, JavaScript converts that value to a string (if it is not already a string) and returns the result of that conversion. [...]
  • If the object has no toString() method, or if that method does not return a primitive value, then JavaScript looks for a valueOf() method. If the method exists, JavaScript calls it. If the return value is a primitive, JavaScript converts that value to a string (if it is not already) and returns the converted value.
  • Otherwise, JavaScript cannot obtain a primitive value from either toString() or valueOf(), so it throws a TypeError.

David Flanagan, JavaScript: The Definitive Guide

Badacadabra
  • 8,043
  • 7
  • 28
  • 49
4

This is happening because of JavaScript's implicit type coersion triggered by the + operator. You can't simply perform a + operation on arrays, so they are converted to strings (which contain the comma-separated string-converted values of the array).

lxe
  • 7,051
  • 2
  • 20
  • 32
-1

Using + will result in a string and the console exclude the brackets. Same as

''+[1,2]
s1mpl3
  • 1,456
  • 1
  • 10
  • 14
  • 3
    It is not the console that excludes the brackets. – Jared Smith Apr 29 '17 at 00:39
  • Maybe not expressed correctly but that is what happens. If you do {uno:11, dos:22} + "" brackets are added. Visually speaking – s1mpl3 Apr 29 '17 at 00:50
  • Um, nope. That gives the default `[object Object]`. We're talking about arrays, not POJOs, and they stringify to comma delimited values. If you want brackets, use `JSON.stringify` instead of string coercion. – Jared Smith Apr 29 '17 at 00:54
  • Do a .length on your [object Object], the brackets are counted despite of being just the visual representation of the object converted to a string – s1mpl3 Apr 29 '17 at 01:00
  • Again, arrays have a custom `toString` implementation, they do not yield the same strings as objects. In fact, one can use this to test for whether an object *is* an array: `Object.prototype.toString.call([1,2]) === '[object Array]'` but `[1,2].toString() === '1,2'`. – Jared Smith Apr 29 '17 at 01:19