block by joyrexus 9810424

Summarization methods for JS arrays

Supplementing Array

JavaScript arrays (as of ES5) have a core set of iteration methods (i.e., methods that apply functions to elements in the array).

While underscore and lodash provide a raft of supplemental collection utilities, there are times when …

For example, groupBy, countBy, and indexBy are extremely convenient tools for many data summarization tasks.

What if you want to use them just as you would map, filter, and reduce?

See array-ext.coffee.md for a demonstration of how Array can be supplemented with the three aforementioned summarization methods and below for usage examples.

Note that we’re just adding methods to Array.prototype. This can cause issues with javascript’s native for..in (enumeration-based) looping, but doesn’t affect coffeescript’s analogous constructs due to how these get compiled down.


groupBy

Groups a list’s values by a criterion. Pass a function that returns the criterion.

isLessThan = (x) ->                                   # grouping function
  (y) -> y < x

expected = 
  true: [1, 2, 3, 4]
  false: [5, 6, 7, 8]

eq [1..8].groupBy(isLessThan(5)), expected


data = ['x', 'xx', 'y', 'yy', 'zzz']
length = (x) -> x.length                              # grouping function

expected = 
  1: ['x', 'y']
  2: ['xx', 'yy']
  3: ['zzz']

eq data.groupBy(length), expected

countBy

Counts instances of a list that group by a certain criterion. Pass a a function that returns the criterion to count by. Similar to groupBy, but instead of returning a list of values, returns a count for the number of values in that group.

parity = (x) -> if x % 2 == 0 then 'even' else 'odd'  # grouping function

expected = 
  odd: 3
  even: 2

eq [1..5].countBy(parity), expected

indexBy

Indexes the object’s values by a criterion. Similar to groupBy, but for when you know that your index values will be unique.

people = [ 
  { id: 100, name: 'Bob' }
  { id: 101, name: 'Ann' }
  { id: 102, name: 'Rex' }
]

expected = { 
  '100': { id: 100, name: 'Bob' }
  '101': { id: 101, name: 'Ann' }
  '102': { id: 102, name: 'Rex' }
}

eq people.indexBy((p) -> p.id), expected

More!

Underscore has some nice sub-libraries for working with arrays.

If you want functions to build arrays, see array.builders.

If you want functions to take things from arrays, see array.selectors

If you want to do multi-level nesting on array elements (i.e., groupBy with hierarchical grouping criteria) see nest.

array-ext.coffee.md

test.coffee