Rebuilding _zip in Javascript using map, arbitrary

2019-08-09 16:59发布

问题:

I'm trying to learn JavaScript well and am practicing rebuilding some underscore functions. I'm trying to rebuild zip using map where there is an arbitrary number of arguments. Here is the solution I came up with, with pluck. I know that the underscore implementation itself uses _pluck, which internally uses Map, so I wanted to see if it was possible to do this with map...

_.zip = function() {

     var argumentsArray = Array.prototype.slice.call(arguments);
     var longestArray = argumentsArray.sort(function(a, b) {
         return b.length - a.length
     })[0];

     //create and return an array that is as long as the longestArray:

     var zipped = Array(longestArray.length);
     // you want to push each element from each array onto an array with the length of the longestArray.

     for (var i = 0; i < longestArray.length; i++) {
         zipped[i] = _.pluck(argumentsArray, i)
     };
     return zipped;
   }

I'm stuck on what to return inside the map function below. I know I have to do some sort of a loop up to the length of the longest element, since the returned array should be that long. How would I do that inside map? Or, should I just do two for loops instead of trying to use map?

 zip = function() {

      //get the longest input argument:
      var argumentsArray = Array.prototype.slice.call(arguments);
      var longestArray = argumentsArray.sort(function(a, b) {
          return b.length - a.length
      })[0];


    //trying to use map here:

    return map(argumentsArray, function(val){
      return ?
    })

  };

  console.log(zip([1, 2, 4], [1])) 
// returns [[1, 1],[2, undefined],[4, undefined]]

回答1:

Below I have attached what should be a working copy of your original implementation without the use of pluck using only maps.

var zip = function() {
  var argumentsArray = Array.prototype.slice.call(arguments);
  var longestArray = argumentsArray.sort(function(a, b) {
    return b.length - a.length
  })[0];

  return longestArray.map(function(value, index, array) {
    return argumentsArray.map(function(val, i, arr) {
      return val[index];
    });
  });
};

The outer map over longestArray acts solely as a looping mechanism so it would be better suited to use a for-loop instead.

The inner loop maps over the array of arguments passed in and, using the current index of the outer map, returns the ith element of each argument array. Since map already returns a new array, each iteration of the inner map will return an array containing the ith elements for each argument array.

Below is another implementation using a for loop and a map.

function zip() {
  //turn args into an array
  var argumentsArray = Array.prototype.slice.call(arguments);
  var returnArr = [];

  //get length of longest array
  var length = argumentsArray.reduce(function (prev, curr) {
    //starter val is 0, if curr array is longer replace with its length
    return (prev >= curr.length) ? prev : curr.length;
  }, 0);

  //push an array of the ith element of each of the argument arrays
  //into the return array
  for (var i = 0; i < length; i++) {
    returnArr.push(argumentsArray.map(function (val) {
      return val[i];
    }));
  }

  return returnArr;
}

Also note that instead of sorting to find the largest array, I reduce over the array lengths to find and return the longest length.

Since js sort is in-place it will potentially change your arguments array order. Your returned array of zipped elements will then be ordered by their original array lengths. Doing it this way they will instead be in the order that the argument arrays were passed in. But both are valid zip implementations depending on what you need.

Hope this helps!