0

If I clone an array and push a value like so:

var arr1 = ["foo", "bar"];
var arr2 = [...arr1];
arr2.push("moo");

console.log(arr2); //["foo", "bar", "moo"]

it behaves as I would expect. However, if I chain the push method like so:

var arr1 = ["foo", "bar"];
var arr2 = [...arr1].push("moo");

console.log(arr2); //3 WTF?

I didn't expect that. Why does chaining .push() like the second example return the number 3 and not an array of values?

k0pernikus
  • 60,309
  • 67
  • 216
  • 347
slynagh
  • 601
  • 4
  • 16
  • 5
    Push doesn't return the Array that it is pushing to. [Consider the API on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push#Return_value). It returns "The new length property of the object upon which the method was called.". – zero298 Jul 26 '19 at 14:51

3 Answers3

1

If you absolutely want to enforce the chaining, you can create your own function on the array prototype. (You should understand its implication though.)

I advise against using the same method name, so invent your own names if you want to go down that route.

Here's an example:

Array.prototype.add = function(elem) {
    this.push(elem)
    return this
}

const myArray = [].add("foo").add("bar")

console.log(myArray) // logs: [ 'foo', 'bar' ]
k0pernikus
  • 60,309
  • 67
  • 216
  • 347
0

array.push returns the new length of the array which you're storing in arr2 variable.

You need the following:

var arr1 = ["foo", "bar"];
var arr2 = arr1.concat("moo");
console.log(arr2); 
mehulmpt
  • 15,861
  • 12
  • 48
  • 88
  • Thank you long day. – slynagh Jul 26 '19 at 14:53
  • 5
    Or just do `var arr2 = [...arr1, "moo"];` – str Jul 26 '19 at 14:53
  • well there are a lot of ways... @str – mehulmpt Jul 26 '19 at 14:54
  • Spreading is inefficient. – Kobe Jul 26 '19 at 14:55
  • @Kobe agreed, spreading is not required – mehulmpt Jul 26 '19 at 14:56
  • @mehulmpt As is concat, though. You should just write `arr2 = arr1; arr2.push("moo")` – Kobe Jul 26 '19 at 14:57
  • @Kobe Nope. With `arr2 = arr1; arr2.push("moo")`, now you're modifying `arr1` as well. – mehulmpt Jul 26 '19 at 14:59
  • @kobe does that not then also mutate arr1? – slynagh Jul 26 '19 at 14:59
  • @mehulmpt, thats true, then i think `arr2 = [...arr1]; arr2.push("moo")` is quicker than concat. Ill write a test. – Kobe Jul 26 '19 at 15:00
  • @slynagh https://jsperf.com/spread-push-vs-concat – Kobe Jul 26 '19 at 15:03
  • @Kobe obviously from your benchmark push is 7x faster than concat on my MBA. But I seriously believe that OP is not going to perform 2 million operations per second for that effect to be visible. For even some 100s of operations per second, the gain is probably in few microseconds. But I appreciate your performance-oriented approach. – mehulmpt Jul 26 '19 at 15:06
  • Don't get me wrong, your solution is absolutely correct, just a lot slower if used in an 10000x loop :P – Kobe Jul 26 '19 at 15:07
  • @str 's `var arr2 = [...arr1, "moo"];` should be the answer – Wilhelmina Lohan Jul 26 '19 at 15:15
  • @Kobe Then why do you post things like "Spreading is inefficient" without having anything to back it up? – str Jul 26 '19 at 15:17
  • @str check my answer lol, I wrote that as I was writing out an answer, since I was going to write spread + push is better than a regular spread. – Kobe Jul 26 '19 at 15:17
  • 1
    GUYS CHILL. @str what I think @Kobe meant was spreading was unnecessary in my solution when I was already using `concat` to create new array as concat doesn't mutate the older array. @Kobe you're right about performance but in a practical scenario, nobody cares as long as you're not writing a critical internal part of any library. Focus on getting things done before heavily optimizing them. – mehulmpt Jul 26 '19 at 15:21
  • @str, I didn't mean to trigger you, I meant spreading *alone* is inefficient as compared to spreading & pushing. I didn't get a chance to edit my comment, and later wrote a test. – Kobe Jul 26 '19 at 15:21
  • @Kobe It was a question, nobody was triggered. – str Jul 26 '19 at 15:30
0

As mehulmpt said, Array.prototype.push() returns the new length of the array after pushing. However, using concat is extremely slow. Instead, spread slice the first array and push the new value on a separate line:

const arr1 = ["foo", "bar"];
const arr2 = arr1.slice();
arr2.push("moo");

console.log(arr1, arr2)

JSPerf here

JSPerf

EDIT: Completely forgot about slice. Slice is faster than spread, use that instead.

Kobe
  • 6,226
  • 1
  • 14
  • 35
  • That's interesting @Kobe but I wonder how concat() stacks up against spread method when dealing with massive or mulitdemensional arrays? – slynagh Jul 26 '19 at 15:12
  • @slynagh I should think it would perform as badly as it does here. It's best to avoid using concat at all costs. – Kobe Jul 26 '19 at 15:14
  • spread and spread push are really close and spread is sometimes faster when I run the test – Wilhelmina Lohan Jul 26 '19 at 15:14
  • @WilliamLohan spread is consistently slower for me by 10% ±3%. Make sure you stay on the same tab while running the tests, otherwise the results will not be accurate. – Kobe Jul 26 '19 at 15:15
  • @Kobe concat is twice as fast on Edge as on Chrome, and wins out against other methods (which are really quite slow). Just goes to show we are ALWAYS at the mercy of client performance, but also debunks statement to never use concat. :) – slynagh Jul 26 '19 at 17:16