array.from vs push (v3)

Revision 3 of this benchmark created on


Setup

// Initialization code (in the setup block of jsperf)
var FakeNode = function(tagName) {
  this.tagName = tagName;
  this.childNodes = [];
  this.addChild = function(node) {
    this.childNodes.push(node);
    node.parent = this;
  };
  this.firstChild = function() {
    return this.childNodes[0] || null;
  };
  this.nextSibling = function() {
    const parent = this.parent;
    if (!parent) return null;
    const index = parent.childNodes.indexOf(this);
    return index !== -1 && index + 1 < parent.childNodes.length
      ? parent.childNodes[index + 1]
      : null;
  };
};

var generateFakeDOMNodes = function(totalNodes, childrenPerNode) {
  var nodes = [];
  for (var i = 0; i < totalNodes; i++) {
    var node = new FakeNode('Node' + i);
    for (var j = 0; j < childrenPerNode; j++) {
      var child = new FakeNode('Child' + i + '-' + j);
      node.addChild(child);
    }
    nodes.push(node);
  }
  return nodes;
};

var nodes = generateFakeDOMNodes(100, 100);

var getChildrenUsingArrayFrom = function(nodes, predicate) {
  return nodes.flatMap(function(node) {
    return Array.from(node.childNodes).filter(function(child) {
      return predicate ? predicate(child) : true;
    });
  });
};

var getChildrenUsingSlice = function(nodes, predicate) {
  return nodes.flatMap(function(node) {
    return [].slice.call(node.childNodes).filter(function(child) {
      return predicate ? predicate(child) : true;
    });
  });
};

var getChildrenUsingPush = function(nodes, predicate) {
  var allChildren = [];
  for (var i = 0; i < nodes.length; i++) {
  	const childNodes = nodes[i].childNodes;
  	for (var j = 0; j < childNodes.length; j++) {
  		const node = childNodes[j];
  		if (predicate) {
  			if (predicate(node)) {
  				allChildren.push(node);
  			}
  		} else {
  			allChildren.push(node);
  		}
  	}
  	return allChildren;
  }
};

var getChildrenPreallocated = function(nodes, predicate = null) {
  // Step 1: Calculate total number of children
  let totalChildren = 0;
  for (let i = 0; i < nodes.length; i++) {
    totalChildren += nodes[i].childNodes.length;
  }

  // Step 2: Preallocate an array with the size equal to the total number of children
  let children = new Array(totalChildren);
  let index = 0;

  // Step 3: Iterate over each node and then over each child
  for (let i = 0; i < nodes.length; i++) {
    for (let child = nodes[i].firstChild(); child !== null; child = child.nextSibling()) {
      if (predicate ? predicate(child) : true) {
        children[index] = child;
        index++;
      }
    }
  }

  // Step 4: Adjust the length of the array to be equal to the number of passed children
  children.length = index;

  return children;
}

var samplePredicate = function(node) {
  return node.tagName.endsWith('0');
};

Test runner

Ready to run.

Testing in
TestOps/sec
Array.from
getChildrenUsingArrayFrom(nodes, samplePredicate);

ready
Push
getChildrenUsingPush(nodes, samplePredicate);
ready
getChildrenPreallocated
getChildrenPreallocated(nodes, samplePredicate);
ready
slice
getChildrenUsingSlice(nodes, samplePredicate);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.