组合用例数组

时间:2016-02-19 20:04:37

标签: javascript arrays node.js testing

Node.js app,编写验证测试。鉴于以下内容:

var obj = { foo: null, bar: null, baz: null},
    values = [ 0, 1];

我需要创建 n 个对象,以便为每个可能值的组合分配每个属性,以表示每个可能的用例。因此,对于此示例,输出应为 2 ^ 3 = 8 个对象,例如

[
    { foo: 0, bar: 0, baz: 0},
    { foo: 0, bar: 1, baz: 0},
    { foo: 0, bar: 1, baz: 1},
    { foo: 0, bar: 0, baz: 1},
    { foo: 1, bar: 0, baz: 0},
    { foo: 1, bar: 1, baz: 0},
    { foo: 1, bar: 1, baz: 1},
    { foo: 1, bar: 0, baz: 1},
]

Underscore或lodash或其他库是可接受的解决方案。理想情况下,我想要这样的事情:

var mapUseCases = function(current, remaining) {
    // using Underscore, for example, pull the current case out of the
    // possible cases, perform logic, then continue iterating through
    // remaining cases
    var result = current.map(function(item) {
        // perform some kind of logic, idk
        return magic(item);
    });
    return mapUseCases(result, _.without(remaining, current));
}

var myValidationHeadache = mapUseCases(currentThing, somethingElse);
请原谅我的伪代码,我想我的脑子已经坏了。 ¯\ _(ツ)_ /¯

3 个答案:

答案 0 :(得分:11)

任何对象长度和任何值的解决方案。

请注意,undefined值不会显示。



function buildObjects(o) {
    var keys = Object.keys(o),
        result = [];

    function x(p, tupel) {
        o[keys[p]].forEach(function (a) {
            if (p + 1 < keys.length) {
                x(p + 1, tupel.concat(a));
            } else {
                result.push(tupel.concat(a).reduce(function (r, b, i) {
                    r[keys[i]] = b;
                    return r;
                }, {}));
            }
        });
    }

    x(0, []);
    return result;
}

document.write('<pre>' + JSON.stringify(buildObjects({
    foo: [0, 1, 2],
    bar: [true, false],
    baz: [true, false, 0, 1, 42]
}), 0, 4) + '</pre>');
&#13;
&#13;
&#13;

答案 1 :(得分:2)

一种方法是在基于values.length的系统中从“000”计数到“999”:

keys = ['foo','bar','baz']
values = ['A', 'B']


width = keys.length
base = values.length
out = []

for(var i = 0; i < Math.pow(base, width); i++) {
  
  var d = [], j = i;
  
  while(d.length < width) {
    d.unshift(j % base)
    j = Math.floor(j / base)
  }
  
  var p = {};
  
  for(var k = 0; k < width; k++)
    p[keys[k]] = values[d[k]]

  out.push(p)
}  


document.write('<pre>'+JSON.stringify(out,0,3))

产品更新:

'use strict';

let
    keys = ['foo', 'bar', 'baz'],
    values = [
        ['A', 'B'],
        ['a', 'b', 'c'],
        [0, 1]
    ];


let zip = (h, t) =>
    h.reduce((res, x) =>
        res.concat(t.map(y => [x].concat(y)))
    , []);

let product = arrays => arrays.length
    ? zip(arrays[0], product(arrays.slice(1)))
    : [[]];

let combine = (keys, values) =>
    keys.reduce((res, k, i) =>
        (res[k] = values[i], res)
        , {});

let z = product(values).map(v => combine(keys, v));

z.map(x => document.write('<pre>'+JSON.stringify(x)+'</pre>'))

答案 2 :(得分:1)

这是您想要的non-recursive版本:

function createRange(keys, values) {
  if (typeof values[0] !== typeof [])
    values = keys.map(k => values);
  var pointer = {};
  var repeats = 1;
  keys.forEach((k, i) => {
    var vLen = values[i].length;
    repeats *= vLen;
    pointer[k] = {
      get value() {
          return values[i][pointer[k].current]
        },
        current: 0,
        period: Math.pow(vLen, i),
        inc: function() {
          var ptr = pointer[k];
          ptr.current++;
          if (ptr.current < vLen) return;

          ptr.current = 0;
          if (i + 1 === keys.length) return;

          var nk = keys[i + 1];
          pointer[nk].inc()
        }
    };
  });
  var result = [];
  for (var i = 0; i < repeats; i++) {
    var o = {};
    result.push(o);
    keys.forEach(k => o[k] = pointer[k].value)
    pointer[keys[0]].inc();
  }
  return result;
}

var objKeys = ['u', 'v', 'w', 'x', 'y', 'z'];
var objValues = [
  ['1', '2', '3'],
  ['a', 'b', 'c'],
  ['foo', 'bar', 'baz'],
  [1, 3, 2],
  ['test', 'try', 'catch'],
  ['Hello', 'World'],
];

var range = createRange(objKeys, objValues);
range.map(v => document.write(JSON.stringify(v).big()))