code: function missingCombinations(data, props) {
  
    if (!Array.isArray(data)) {
      throw new TypeError('data should be an array');
    }
  
    props.forEach(prop => {
      if (!prop.key) {
        throw new TypeError('prop missing key');
      }
      if (!Array.isArray(prop.valueRange)) {
        throw new TypeError(`Invalid valueRange for prop with key ${prop.key}`);
      }
      if (prop.valueRange.length == 0) {
        throw new TypeError(`Length is zero for valueRange with prop ${prop.key}`);
      }
    });
  
    var params = props.map(item => item.valueRange);
    var expectedCombinations = Combinatorics.cartesianProduct.apply(null, params).toArray();
  
    var presentCombinations = _.uniq(data, item => props.reduce(
      (prev, curr) => prev + '-' + item[curr.key] ), '').map(item => {
        return props.reduce((p, c) => {
          p.push(item[c.key]);
          return p;
        }, []);
      }
    );
    return _.differenceBy(expectedCombinations, presentCombinations, arr => {
      return arr.reduce((prev, curr) => prev + '-' + curr, '');
    });
  }
docs: /*
  Find out which combinations of specified
  property values do not exist in `data`
  
  props is array of property definitions which are
  expected to exist. Each object in the array has
  the following attributes:
  
  key        -- data object attribute name
  valueRange -- range of values expected for key