Design & Download
Your JS Library
in Seconds!!!

Snippet: indexBy() - Indexing an Array/Object By New Criteria

Written by Christopher West (cwest) on December 17, 2015.
Create or extend an object basing its keys and values off of another array or object.
function indexBy(arrOrObj, opt_key, opt_indexAsArrays, opt_objToExtend) {
  var k, hasNoKey = opt_key == undefined, keyIsFn = typeOf(opt_key, 'Function');
  opt_objToExtend = opt_objToExtend || {};
  walk(arrOrObj, function(t) {
    k = keyIsFn
      ? opt_key.call(opt_objToExtend, t, arrOrObj)
      : hasNoKey
        ? t
        : t[opt_key];
    if (k != undefined) {
      if (opt_indexAsArrays) {
        if (!(k in opt_objToExtend)) {
          opt_objToExtend[k] = [];
        }
        else if (!typeOf(opt_objToExtend[k], 'Array')) {
          opt_objToExtend[k] = [opt_objToExtend[k]];
        }
        opt_objToExtend[k].push(t);
      }
      else {
        opt_objToExtend[k] = t;
      }
    }
  });
  return opt_objToExtend;
}

Example - Hashing Numbers

Let's say that we have an array of numbers that we would like to put into buckets depending on the remainder when they are divided by 3:

var numbers = [1,5,32,8,51,0];
var buckets3 = YourJS.indexBy(numbers, function(num) {
  return num % 3;
}, true, []);
// > [[51,0],[1],[5,32,8]]

Example - Getting All Elements By Node Name

Let's say that you want to get all of the elements on the page indexed by their nodeName. This is how you could get it done:

var allElems = document.getElementsByTagName('*');
var elemsByNodeName = YourJS.indexBy(allElems, 'nodeName', true);

One thing to keep in mind is that in most modern browsers, the nodeName property values are capitalized, therefore if we wanted to store all of the elements by their nodeName but in lowercased form I could do this:

var allElems = document.getElementsByTagName('*');
var elemsByNodeName = YourJS.indexBy(allElems, function(elem) {
  return elem.nodeName.toLowerCase();
}, true);

indexBy(...) API Documentation

Description

Create or extend an object basing its keys and values off of another array or object.

Parameters

  1. arrOrObj {Array|Object}:
    All of the items in the array, array-like object, or other type of object will be iterated over and each item found with a specified key will be indexed in a new object (or in opt_objToExtend if it is specified).
  2. opt_key {Function|number|string|undefined}:
    • If a number or a string is given...
      the value at that index in each item of arrOrObj will be used to index the item in the returned object (AKA opt_objToExtend).
    • If a function (function(this: opt_objToExtend, value, arrOrObj) -> opt_key) is given...
      this function will be used to generate the actual key from which the item will be retrieved and used as the key to store this item in opt_objToExtend. In other words opt_objToExtend[opt_key.call(opt_objToExtend, item, arrOrObj)] is what will be used to store the item in each iteration. If this function returns undefined or a key which resolves to an undefined key then the iteration's item will be omitted.
    • If not specified or undefined is given, eacn item in arrOrObj will be used to itself in the returned object (AKA opt_objToExtend).
  3. opt_indexAsArrays {boolean=}:
    Optional. Defaults to false. If true, each item that is indexed in the returned object will be indexed within a array so that collisions will result in an array of more than one item. If false, each item that is index in the returned object will be indexed as the found item, but in the case of multiple items resolving to the same index, only the last item will be found in the returned object.
  4. opt_objToExtend {?}:
    Optional. Defaults to a new empty object ({}). This will be the object that is modified and returned with the desired keys and values.

Returns

Returns opt_objToExtend (or a new object in the case that it wasn't specified) containing items from arrOrObj in the desired indices.