Counting predicates

How to find the first item after the second number in an array.

It is simple to find every number in the given Array

function isNumber(x) {
  return typeof x === 'number';
}
var list = ['a', 'b', 1, 2, 3, 'c'];
console.log(list.filter(isNumber)); // [1, 2, 3]

The .filter iterator returns every value that passes the given predicate. What if we wanted to know what the second number is in an array? We could filter and access element with index 1.

function nth(k, predicate, list) {
  return list.filter(isNumber)[k];
}
var secondNumber = nth.bind(null, 1, isNumber);
console.log(secondNumber(list)); // 2

This approach destroys the information about the position of the second number in the original list. What if I wanted to print first item after the second number? By filtering the list first, we lost the original positions.

Counting predicates

Instead of making a new array right away, we can write a function that keeps count how many times a predicate returned true.

function nth(n, predicate) {
  var k = 0;
  return function () {
    var result = predicate.apply(null, arguments);
    if (result) {
      k += 1;
    }
    return k === n;
  };
}

Instead of using Array.prototype.filter we can now use Array.prototype.map because we are interested where in the original list is the second number, not its value yet.

var list = ['a', 'b', 1, 2, 3, 'c'];
var secondNumber = nth(2, isNumber);
console.log(list.map(secondNumber));
// [ false, false, false, true, false, false ]

Function nth mixes two aspects togehter: counting and predicate execution. I showed an example of splitting such function into smaller focused functions. Let us rewrite nth in terms of two simpler functions. First, the counter.

// keeps number of executions
function counter(n) {
  console.assert(n > 0, 'invalid n to count to ' + n);
  var k = 0;
  return function () {
    k += 1;
    return k === n;
  }
}
// example
var countTo2 = counter(2);
console.assert(!countTo2(), 'first');
console.assert(countTo2(), 'second');
console.assert(!countTo2(), 'after');

The nth function becomes a simple composition

function nth(n, predicate) {
  var counterToN = counter(n);
  return function () {
    var result = predicate.apply(null, arguments);
    return result ? counterToN() : false;
  };
}
var secondNumber = nth(2, isNumber);
var list = ['a', 'b', 1, 2, 3, 'c'];
console.log(list.map(secondNumber));
// [ false, false, false, true, false, false ]

Using counter we can write other predicates, for example for finding items n positions after another predicate returns true.

function after(n, predicate) {
  var countToN = counter(n);
  var seenPositive;
  return function () {
    if (seenPositive) {
      return countToN();
    }
    seenPositive = predicate.apply(null, arguments);
    return false;
  };
}

A couple of examples

var list = ['a', 'b', 1, 2, 3, 'c'];
var firstAfter2ndNumber = after(1, nth(2, isNumber));
console.log(list.map(firstAfter2ndNumber));
[ false, false, false, false, true, false ]
var secondAfter2ndNumber = after(2, nth(2, isNumber));
console.log(list.map(secondAfter2ndNumber));
[ false, false, false, false, false, true ]

Note that our predicate with built-in counter does NOT reset itself. Thus it can only be used once

var list = ['a', 'b', 1, 2, 3, 'c'];
var firstAfter2ndNumber = after(1, nth(2, isNumber));
console.log(list.map(firstAfter2ndNumber));
[ false, false, false, false, true, false ]
console.log(list.map(firstAfter2ndNumber));
[ false, false, false, false, false, false ]

Printing value

Once we have boolean list, we can find the actual value. Just take the first element where the predicate returns true

1
2
3
4
5
6
7
8
9
10
11
12
13
function toValue(list, predicate) {
var value;
list.some(function (x, k) {
if (predicate(x, k)) {
value = x;
return true;
}
});
return value;
}
firstAfter2ndNumber = after(1, nth(2, isNumber));
var list = ['a', 'b', 1, 2, 3, 'c'];
console.log(toValue(list, firstAfter2ndNumber)); // 3

I probably will use this approach for flexible optional argument parsing.