Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging two object (NodeList) arrays in JavaScript

I am attempting to merge two arrays of objects to so I can validate a form. The usual concat method does not appear to work in this circumstance. Concat works with ordinary numerical and string arrays but doesn't with object arrays. The line var allTags = allInputs.concat(allSelects); does not work.

var allInputs = document.getElementsByTagName("input");
alert("Inputs: " + allInputs.length);

var allSelects = document.getElementsByTagName("select");
alert("Selects: " + allSelects.length);

var allTags = allInputs.concat(allSelects);
alert("allTags: " + allTags.length);
like image 587
user2035797 Avatar asked Feb 02 '13 18:02

user2035797


People also ask

What is the difference between NodeList and array?

A NodeList may look like an array, but in reality, they both are two completely different things. A NodeList object is basically a collection of DOM nodes extracted from the HTML document. An array is a special data-type in JavaScript, that can store a collection of arbitrary elements.

How do I merge two arrays of objects in Node JS?

To merge elements from one array to another, we must first iterate(loop) through all the array elements. In the loop, we will retrieve each element from an array and insert(using the array push() method) to another array. Now, we can call the merge() function and pass two arrays as the arguments for merging.

How do I merge two objects with the same ID?

To merge objects by id with JavaScript, we use the map method and the spread operator. const a1 = [ { id: 1, name: "test" }, { id: 2, name: "test2" }, ]; const a2 = [ { id: 1, count: "1" }, { id: 2, count: "2" }, ]; const a3 = a1.


2 Answers

Concat works with ordinary numerical and string arrays but doesn't with object arrays.

Actually, it does, but NodeList instances don't have a concat method, and Array#concat doesn't have a means of identifying that you want to flatten those (because they're not arrays).

But it's still fairly easy to do (see caveat below, though). Change this line:

var allTags = allInputs.concat(allSelects);

to

var allTags = [];
allTags.push.apply(allTags, allInputs);
allTags.push.apply(allTags, allSelects);

Live Example | Source

That works by using a bit of a trick: Array#push accepts a variable number of elements to add to the array, and Function#apply calls the function using the given value for this (in our case, allTags) and any array-like object as the arguments to pass to it. Since NodeList instances are array-like, push happily pushes all of the elements of the list onto the array.

This behavior of Function#apply (not requiring the second argument to really be an array) is very clearly defined in the specification, and is well-supported in modern browsers.

Sadly, IE6 and 7 don't support the above (I think it's specifically using host objects — NodeLists — for Function#apply's second argument), but then, we shouldn't be supporting them, either. :-) IE8 doesn't, either, which is more problematic. IE9 is happy with it.

If you need to support IE8 and earlier, sadly, I think you're stuck with a boring old loop:

var allInputs = document.getElementsByTagName('input');
var allSelects = document.getElementsByTagName('select');
var allTags = [];

appendAll(allTags, allInputs);
appendAll(allTags, allSelects);

function appendAll(dest, src) {
  var n;

  for (n = 0; n < src.length; ++n) {
    dest.push(src[n]);
  }

  return dest;
}

Live Example | Source

That does work on IE8 and earlier (and others).

like image 62
T.J. Crowder Avatar answered Sep 17 '22 15:09

T.J. Crowder


document.get[something] returns a NodeList, which looks very similar to an Array, but has numerous distinctive features, two of which are:

  • It does not contain the concat method
  • The list of items is live. This means they change as the document changes in real time.

If you don't have any issues with turning your NodeLists into actual arrays, you could do the following to achieve the effect you desire:

var allInputs  = document.getElementsByTagName("input")
  , allSelects = document.getElementsByTagName("select")
;//nodeLists

var inputList  = makeArray(allInputs)
  , selectList = makeArray(allSelects)
;//nodeArrays

var combined   = inputList.concat(selectList);

function makeArray(list){
  return Array.prototype.slice.call(list);
}

You will lose any and all behaviors of the original NodeList, including it's ability to update in real time to reflect changes to the DOM, but my guess is, that's not really an issue.

like image 34
THEtheChad Avatar answered Sep 18 '22 15:09

THEtheChad