<commit_before>
/*
usage:

asyncMap(myListOfStuff, function (thing, cb) { doSomething(thing, cb) }, cb)
asyncMap(list, function (l, cb) { foo(l, cb) ; bar(l, cb) }, 2, cb)

*/

module.exports = asyncMap

function asyncMap (list, fn, n, cb_) {
  if (typeof n === "function") cb_ = n, n = 1
  if (typeof cb_ !== "function") throw new Error(
    "No callback provided to asyncMap")
  if (!list || !list.length) return cb_(null, [])
  var data = []
    , errState = null
    , l = list.length
    , a = l * n
  function cb (er, d) {
    if (errState) return
    if (arguments.length > 1) data = data.concat(d)
    // see if any new things have been added.
    if (list.length > l) {
      var newList = list.slice(l)
      a += (list.length - l) * n
      l = list.length
      process.nextTick(function () {
        newList.forEach(function (ar) { fn(ar, cb) })
      })
    }
    if (er) {
      if (false === cb_(errState = er, data)) errState = null
    } else if (-- a === 0) cb_(errState, data)
  }
  list.forEach(function (ar) { fn(ar, cb) })
}
<commit_msg>Rather than take a number to do more than one thing, also accept taking a list of functions to call on each thing.<commit_after>
/*
usage:

// do something to a list of things
asyncMap(myListOfStuff, function (thing, cb) { doSomething(thing.foo, cb) }, cb)
// do more than one thing to each item
asyncMap(list, fooFn, barFn, cb)
// call a function that needs to go in and call the cb 3 times
asyncMap(list, callsMoreThanOnce, 3, cb)

*/

module.exports = asyncMap

function asyncMap (list) {
  var steps = Array.prototype.slice.call(arguments)
    , list = steps.shift() || []
    , cb_ = steps.pop()
  if (typeof cb_ !== "function") throw new Error(
    "No callback provided to asyncMap")
  if (!Array.isArray(list)) list = [list]
  var n = (typeof steps[steps.length - 1] === "number")
        ? steps.pop()
        : steps.length
    , data = []
    , errState = null
    , l = list.length
    , a = l * n
  if (!a) return cb_(null, [])
  function cb (er, d) {
    if (errState) return
    if (arguments.length > 1) data = data.concat(d)
    // see if any new things have been added.
    if (list.length > l) {
      var newList = list.slice(l)
      a += (list.length - l) * n
      l = list.length
      process.nextTick(function () {
        newList.forEach(function (ar) {
          steps.forEach(function (fn) { fn(ar, cb) })
        })
      })
    }
    // allow the callback to return boolean "false" to indicate
    // that an error should not tank the process.
    if (er) {
      if (false === cb_(errState = er, data)) errState = null
    } else if (-- a === 0) cb_(errState, data)
  }
  // expect the supplied cb function to be called
  // "n" times for each thing in the array.
  list.forEach(function (ar) {
    steps.forEach(function (fn) { fn(ar, cb) })
  })
}
{'fraction_non_alphanumeric': 0.146779303062302, 'fraction_numerical': 0.0031678986272439284, 'mean_word_length': 2.5748427672955976, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '317765', 'n_tokens_mistral': 965, 'n_tokens_neox': 955, 'n_words': 326}