125

So I've created this jqueryui widget. Its creates a div that I can stream errors into. The widget code looks like this:

$.widget('ui.miniErrorLog', {
   logStart: "<ul>",   // these next 4 elements are actually a bunch more complicated.
   logEnd:   "</ul>",
   errStart: "<li>",
   errEnd:   "</li>",
   content:  "",
   refs:     [],

   _create: function() { $(this.element).addClass( "ui-state-error" ).hide(); },

   clear: function() { 
      this.content = ""; 
      for ( var i in this.refs )
         $( this.refs[i] ).removeClass( "ui-state-error" );
      this.refs = [];
      $(this.element).empty().hide(); 
   }, 

   addError: function( msg, ref ) {
      this.content += this.errStart + msg + this.errEnd; 
      if ( ref ) {
         if ( ref instanceof Array )
            this.refs.concat( ref );
         else
            this.refs.push( ref );
         for ( var i in this.refs )
            $( this.refs[i] ).addClass( "ui-state-error" );
      }
      $(this.element).html( this.logStart + this.content + this.logEnd ).show();
   }, 

   hasError: function()
   {
      if ( this.refs.length )
         return true;
      return false;
   },
});

I can add error messages into it, and references to page elements that is will put into an error state. I use it to validate dialogs. In the "addError" method I can pass in a single id, or an array of ids, like this:

$( "#registerDialogError" ).miniErrorLog( 
   'addError', 
   "Your passwords don't match.", 
   [ "#registerDialogPassword1", "#registerDialogPassword2" ] );

But when I pass in an array of id's it doesn't work. The problem is in the following lines (i think):

if ( ref instanceof Array )
   this.refs.concat( ref );
else
   this.refs.push( ref );

Why doesn't that concat work. this.refs and ref are both arrays. So why doesn't the concat work?

Bonus: am I doing anything else dumb in this widget? It's my first one.

Rafael Baptista
  • 11,181
  • 5
  • 39
  • 59
  • [JavaScript: How to join / combine two arrays to concatenate into one array?](https://stackoverflow.com/q/3975170/86967) – Brent Bradburn Jun 13 '18 at 04:18
  • Very similar (for arrays instead of strings): ["concat" does not join JavaScript arrays together? - Stack Overflow](https://stackoverflow.com/questions/12810366/concat-does-not-join-javascript-arrays-together) – user202729 Mar 01 '21 at 00:00

8 Answers8

339

The concat method doesn't change the original array, you need to reassign it.

if ( ref instanceof Array )
   this.refs = this.refs.concat( ref );
else
   this.refs.push( ref );
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
Alcides Queiroz
  • 9,456
  • 3
  • 28
  • 43
  • 8
    That did it. I would have thought that a concat method on an object would append to the object. But I guess that's not how it works. – Rafael Baptista Oct 09 '12 at 15:44
  • 4
    @Rafael: The `push` method does that, you could do `[].push.apply(this.refs, ref)` – Bergi Oct 09 '12 at 15:48
89

Here is the reason why:

Definition and Usage

The concat() method is used to join two or more arrays.

This method does not change the existing arrays, but returns a new array, containing the values of the joined arrays.

You need to assign the result of the concatenation back in the array that you have.

Konstantin Dinev
  • 34,219
  • 14
  • 75
  • 100
24

To expand on Konstantin Dinev:

.concat() doesn't add to current object, so this will not work:

foo.bar.concat(otherArray);

This will:

foo.bar = foo.bar.concat(otherArray);
mewc
  • 1,253
  • 1
  • 15
  • 24
16

you have to re-assign value using = to array , that you want to get concated value

let array1=[1,2,3,4];
let array2=[5,6,7,8];

array1.concat(array2);
console.log('NOT WORK :  array1.concat(array2); =>',array1);

array1= array1.concat(array2);
console.log('WORKING :  array1 = array1.concat(array2); =>',array1);
Saurabh Mistry
  • 12,833
  • 5
  • 50
  • 71
2
dataArray = dataArray.concat(array2)
double-beep
  • 5,031
  • 17
  • 33
  • 41
PRATHYUSH P
  • 175
  • 1
  • 5
  • Thanks for the answer, but this doesn't offer anything on top of existing answers that are nearly a decade old and provide a good deal more information. Generally, please just upvote one of those unless you have something novel to add. – ggorlen Sep 02 '22 at 20:10
2

Others have mentioned that this.refs.concat(ref); allocates and returns a new array which can be reassigned to the object: this.refs = this.refs.concat(ref);. concat does not modify either argument array.

However, probably more accurate here is to use push, which adds an element to the calling array in-place: this.refs.push(ref); (no reassignment with =--push returns the new array length which is usually ignored).

If you're adding multiple items, push accepts variable arguments, so you can spread an array onto it:

const arr = [0, 1, 2];
arr.push(3); // add one element
console.log(arr) // => [0, 1, 2, 3]
arr.push(4, 5); // add two elements
console.log(arr) // => [0, 1, 2, 3, 4, 5]
const toAdd = [6, 7, 8];
arr.push(...toAdd); // add an array
console.log(arr); // => [0, 1, 2, 3, 4, 5, 6, 7, 8]

concat could create a similar result with reassignment:

let arr = [0, 1, 2];
arr = arr.concat(3); // reassign with one new element
console.log(arr) // => [0, 1, 2, 3]
arr = arr.concat(4, 5); // reassign with two new elements
console.log(arr) // => [0, 1, 2, 3, 4, 5]
const toAdd = [6, 7, 8];
arr = arr.concat(toAdd); // reassign with a new array added
console.log(arr); // => [0, 1, 2, 3, 4, 5, 6, 7, 8]

concat is clearly less optimal for this use case but is useful to know about for others, particularly when immutability is needed (for example, when working with React state).

Another occasion mutating with push is handy is in a function that should mutate its argument:

const addOne = arr => { // contrived example
  arr.push(1);
};
const arr = [];
addOne(arr);
console.log(arr); // => [1] as expected

const addOneBroken = arr => {
  arr = arr.concat(1); // purely local reassignment!
};
addOneBroken(arr);
console.log(arr); // => still [1]

Another option for merging arrays and items is the spread syntax, similar to concat:

let arr = [0, 1, 2];
const toAdd = [3, 4, 5];
const arr1 = [...arr, 3]; // add one item, similar to concat
console.log(arr1); // [0, 1, 2, 3]
const arr2 = [...arr, ...toAdd]; // add an array, similar to concat
console.log(arr2); // [0, 1, 2, 3, 4, 5]
arr = [-1, ...arr, 42, ...toAdd, 6, 7]; // build a new complex array and reassign
console.log(arr); // => [-1, 0, 1, 2, 42, 3, 4, 5, 6, 7]

The above can be done with chained concat calls:

let arr = [0, 1, 2];
const toAdd = [3, 4, 5];
arr = [-1].concat(arr).concat(42).concat(toAdd).concat(6).concat(7);
console.log(arr); // => [-1, 0, 1, 2, 42, 3, 4, 5, 6, 7]

Note that if you have an array you don't want to flatten during a push, just skip the ... spread:

const arr = [0, 1, 2];
const toAdd = [3, 4, 5];
arr.push(toAdd);
console.log(arr); // => [0, 1, 2, [3, 4, 5]]
ggorlen
  • 44,755
  • 7
  • 76
  • 106
1

The concat method doesn't change the original array, you can use array destructuring.

const numbers = [1,2,3,4];
const newNumbers = [5,6,7,8,9];

numbers.push(...newNumbers); // [1,2,3,4,5,6,7,8,9]
Paiman Rasoli
  • 1,088
  • 1
  • 5
  • 15
0

Just a note, if you really want to have a mutable array when using the concat function (by mutable I mean that it does not create a new array but mutate the existing one) you can reassign the concat function for that array instance. There is what I did when I needed this.

let myArray = [];

myArray.concat= function(  toAdd){
     if(Array.isArray(toAdd)){
        for(let node of toAdd)
             this.push(node);
      }else
        this.push(toAdd);
}
Andrew S
  • 2,847
  • 3
  • 33
  • 50