简体   繁体   中英

Rewriting Javascript filter / forEach

New to Javascript - I am attempting to rewrite forEach and filter to understand them. I'd like to be able to use filter to pass something like {"hello": 4, "world": 2, "hi": 1} and be able to filter based on the number.

Here's my forEach:

function myForEach(collection, callback) {

      if (Array.isArray(collection)) {
        for (var i = 0; i < collection.length; i++) {
          callback(collection[i]);
        }
      }
      else {
        for (var key in collection) {
          callback(collection[key]);
        }
      }
    }

Here's filter:

function filterWithForEach (collection, predicate) {
  if (Array.isArray(collection)) {
    var newArray = [];
    myForEach(collection, function (element) {
      if (predicate(element)) {
        newArray.push(element);
      }
    });
    return newArray;
  }
  else {
    var newCollection = {};
    myForEach(collection, function (element) {
      if (predicate(element)) {
        newCollection[element] = element; //here's where I think it's wrong
      }
    });
    return newCollection;
  }
}

I know the issue is with how I am assigning them because when I test it I get these outputs:

console.log(filterWithForEach([1,2,3,4,5], function(num) {
  return num > 2;
})); // works fine
console.log(filterWithForEach(aList, function(item) {
  return item > 3;
})); // provides {4: 4}..

If you want to know what they do, the spec is fairly clear and can pretty reasonably be turned into JavaScript code (in fact, that's been done on MDN ).

Some differences between yours and JavaScript's:

  1. JavaScript's forEach and filter don't use for-in , which is the fundamental difference between yours and JavaScript's. They just work with the object and array indexes, expecting the object to be a array-like (eg, have a length and properties with keys like "0" , "1" , and so on [and yes, all keys are strings, even the keys in standard arrays, which aren't really arrays at all).

  2. JavaScript's version doesn't call the callback for entries that don't exist (eg, in the case of sparse arrays). To add that to yours, you'd add hasOwnProperty(index) at some stage.

  3. JavaScript's version passes more arguments to the callbacks.

  4. JavaScript's version lets you specify a value to use as this when calling the callbacks.

  5. JavaScript's version grabs the length before starting, so if the collection is modified, the old length is used. Depending on where the changes are made, entries might be skipped.

So for instance, your take on forEach might look more like this:

function myForEach(collection, callback, thisArg) {
    var l = +collection.length;
    for (var i = 0; i < l; i++) {
        if (collection.hasOwnProperty(i)) {
            callback.call(thisArg, collection[i], i, collection);
        }
    }
}

Again, that's not (remotely) an accurate implementation of the algorithm in the spec, just a slight modification to yours to address the specific points I raised above.

If you are looking for an object to be returned then this is the way it should be.

function myForEach(collection, callback) {

      if (Array.isArray(collection)) {
        for (var i = 0; i < collection.length; i++) {
          callback(collection[i]);
        }
      }
      else {
        for (var key in collection) {
          callback(collection[key], key);
        }
      }
    }

Here's filter:

function filterWithForEach (collection, predicate) {
  if (Array.isArray(collection)) {
    var newArray = [];
    myForEach(collection, function (element) {
      if (predicate(element)) {
        newArray.push(element);
      }
    });
    return newArray;
  }
  else {
    var newCollection = {};
    myForEach(collection, function (element,key) {
      if (predicate(element)) {
        newCollection[key] = element;           }
    });
    return newCollection;
  }
}

You can creat a filter own method into the Array prototype function. If you are looking for a different approach you can use Iterators and a recursive fuction like this:

Array.prototype.megaFilter = function(cb) {
  const iterator = this[Symbol.iterator]();
  const iteration = iterator.next();
  const filteredArray = [];
  iterate(iteration);
  function iterate(iteration) {
    if (iteration.done) return iteration;
    if (cb(iteration.value)) filteredArray.push(iteration.value);
    const nextIteration = iterator.next(iteration.value);
    return iterate(nextIteration);
  }
  return filteredArray;
};

const array = [1, 2, 3, 4, 5, 6, 7];

console.log(array.megaFilter(v => v % 2 === 0));

The gist for that is here, you are very welcome to give me feedback: https://gist.github.com/jdtorregrosas/d69f67e8079f82fbc2a5904e76a8fb6c

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM