2014-11-02 1 views
5

У меня есть объект следующего вида (упрощенный тестовый пример ниже)декартово произведение яваскрипта свойств объекта

var test = { 
     shirts: { 
      sizes: ['large', 'medium'] 
      ,colors:['red', 'blue'] 
     } 
     , trousers: { 
      type: ['formal', 'casual'] 
      , pattern: ['plaid', 'stripes'] 
     } 
    }; 

Я хочу, чтобы сгенерировать декартово произведение свойств, так что выход представляет собой массив из следующих форма:

// desired output 

[ {shirts:{sizes:'large', color:'red'}, trousers:{type:'formal', pattern:'plaid'}} 
    ,{shirts:{sizes:'large', color:'red'}, trousers:{type:'formal', pattern:'stripes'}} 
    ,{shirts:{sizes:'large', color:'red'}, trousers:{type:'casual', pattern:'plaid'}} 
    , {shirts:{sizes:'large', color:'red'}, trousers:{type:'casual', pattern:'stripes'}} 
    ,{shirts:{sizes:'large', color:'blue'}, trousers:{type:'formal', pattern:'plaid'}} 
..... and so on ] 

Как я могу это достичь? Я разработал следующий код (основанный на модификации кода для декартова продукта массива из другого сообщения SO), но, похоже, я завязываю себя в узлах, пытаясь заставить это работать.

function myCartesianProduct(input, current) { 
    if (!input) { return []; } 


    var head = input[Object.keys(input)[0]]; 

    var tail = objSlice(input); 

    var output = []; 


    for (var key in head) { 

     for (var i = 0; i < head[key].length; i++) { 

      var newCurrent = copy(current); 

      newCurrent[key] = head[key][i]; 


      if (Object.keys(tail).length) { //if tail.length 
       var productOfTail = 
         myCartesianProduct(tail, newCurrent); 
       output = output.concat(productOfTail); 

      } else { 
       output.push(newCurrent); 

      } 
     } 
    } 
    return output; 
} 


function objSlice(obj) { 
    var slicedObj = angular.copy(obj); // copy object using angularJs copy method 
    delete slicedObj[Object.keys(slicedObj)[0]]; //delete the first key 
    return slicedObj; 
}; 

function copy(obj) { 
     var res = {}; 
     for (var p in obj) res[p] = obj[p]; 
     return res; 
    } 

console.log(myCartesianProduct(test)); 

Заранее благодарим за помощь!

+0

См. Http://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript – Paul

+0

@Paul, этот случай отличается. Я видел другие сообщения на этом (и создал код на основе модификации), но есть разница в том, что в этом случае у нас есть свойства вложенных объектов, а не массив массивов. – Jarnal

+0

Да, я думал, возможно, вы могли бы объединить Object.keys() на под-объектах с функцией для декартова произведения массивов в другом вопросе, а затем реструктурировать вывод из массива массивов в массив объектов, скажем 'map' – Paul

ответ

6

Хорошо, давайте начнем с функцией, которая генерирует произведение заданных массивов:

function product(args) { 
    if(!args.length) 
     return [[]]; 
    var prod = product(args.slice(1)), r = []; 
    args[0].forEach(function(x) { 
     prod.forEach(function(p) { 
      r.push([x].concat(p)); 
     }); 
    }); 
    return r; 
} 

Следующий один использует product преобразовать что-то вроде {a:[1,2], b:[3,4]} в [{a:1,b:3},{a:1,b:4},{a:2,b:3},{a:2,b:4}]:

function objectProduct(obj) { 
    var keys = Object.keys(obj), 
     values = keys.map(function(x) { return obj[x] }); 

    return product(values).map(function(p) { 
     var e = {}; 
     keys.forEach(function(k, n) { e[k] = p[n] }); 
     return e; 
    }); 
} 

Для вашего тестовых данных , вы должны применять его дважды:

var result = {}; 
Object.keys(test).forEach(function(k) { 
    result[k] = objectProduct(test[k]) 
}); 

result = objectProduct(result); 

Это дает вам результат, который вы хотели.

+0

спасибо, спасибо, спасибо! – Jarnal