idiomatic use of pickBy with object literal instead of function predicate

Viewed 23

There appears to be an idiomatic use of lodash pickBy utility function whereby instead of supplying a boolean predicate function as the second argument, an object literal is supplied instead. E.g. https://stackoverflow.com/a/51744183/274677

Animals = [{Name: 'Dog', Id: 0},
      {Name: 'Cat', Id: 1},
      {Name: 'Mouse', Id: 2},
      {Name: 'Horse', Id: 3},
      {Name: 'Pig', Id: 3}]
Indexes = _.keys(_.pickBy(Animals, {Id: 3}))

output:

Indexes = ["3", "4"]

This use is undocumented in lodash: https://lodash.com/docs/4.17.15#pickBy

What is the origin of this idiom and why does it work?

1 Answers

Why does it work?

Looking at the compiled lodash's code we can see that the pickBy looks like that:

function pickBy(object, predicate) {
  if (object == null) {
    return {};
  }
  var props = arrayMap(getAllKeysIn(object), function(prop) {
    return [prop];
  });
  predicate = getIteratee(predicate);
  return basePickBy(object, props, function(value, path) {
    return predicate(value, path[0]);
  });
}

predicate (in our example {Id: 3}) is passed to the getIteratee function which looks like that:

function getIteratee() {
  var result = lodash.iteratee || iteratee;
  result = result === iteratee ? baseIteratee : result;
  return arguments.length ? result(arguments[0], arguments[1]) : result;
}

Now, looking at the iteratee:

function iteratee(func) {
  return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG));
}

And baseIteratee:

function baseIteratee(value) {
  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
  if (typeof value == 'function') {
    return value;
  }
  if (value == null) {
    return identity;
  }
  if (typeof value == 'object') {
    return isArray(value)
      ? baseMatchesProperty(value[0], value[1])
      : baseMatches(value);
  }
  return property(value);
}

We know that our argument is an object, but not an array, so few more functions deeper we can find the actual function that is returned:

function getMatchData(object) {
  var result = keys(object),
    length = result.length;

  while (length--) {
    var key = result[length],
      value = object[key];

    result[length] = [key, value, isStrictComparable(value)];
  }
  return result;
}

which just goes through an array and checks if the value is equal

Why is it not documented?

I have no idea, sorry

Related