简体   繁体   中英

Generate permutations of Javascript object keys/values

I need to create a nested hierarchy of options. The options are keys on an array with several sub-options as nested objects.

I need to generate a nested hierarchy from this object.

Starting with an object like this:

const starterObject = {
    id1: {
        1: { value: "A" },
        2: { value: "B" },
        3: { value: "C" },
    },
    id2: {
        1: { value: 10 },
        2: { value: 20 },
    },
};

I need to end up with an object of permutations like this:

const permutations2 = {
    1: [{ value: "A" }, { value: 10 }],
    2: [{ value: "A" }, { value: 20 }],
    3: [{ value: "B" }, { value: 10 }],
    4: [{ value: "B" }, { value: 20 }],
    5: [{ value: "C" }, { value: 10 }],
    6: [{ value: "C" }, { value: 20 }],
};

I tried something like this:

 const starterObject = { id1: { 1: { value: "A" }, 2: { value: "B" }, 3: { value: "C" }, }, id2: { 1: { value: 10 }, 2: { value: 20 }, }, }; const permutationMatrix = []; Object.keys(starterObject["id1"]).forEach(key2 => Object.keys(starterObject["id2"]).forEach(key1 => permutationMatrix.push([ starterObject["id1"][key2], starterObject["id2"][key1], ]) ) ); console.log(permutationMatrix) 

But the problem is that the keys are hardcoded. The actual object will have 1-5 keys (id1 - id5) and any number of nested objects.

I think this will require recursion, but I'm not sure how to proceed from here.

Reduce, entries, and values can help and there is no need for recursion.

 const starterObject = { id1: { 1: { value: "A" }, 2: { value: "B" }, 3: { value: "C" }, }, id2: { 1: { value: 10 }, 2: { value: 20 }, }, id3: { 1: { value: 100 } } }; var entries = Object.entries(starterObject) var out = entries.reduce((arr, group) => { const itms = Object.values(group)[1] const vals = Object.values(itms) // if first time, set up the initial arrays with first set of data if (!arr.length) { return vals.map(v => [v]) } // after first one, we will just loop over the arrays // and start adding the next set of data to each one return arr.reduce((updated, curr) => { vals.forEach(val => { // make copy so we are not adding data to the reference const copy = curr.slice() copy.push(val) updated.push(copy) }) return updated }, []) }, []) console.log(JSON.stringify(out)) 

You could use recursion for this. Your input/output format is somewhat peculiar with its array-like objects, with keys starting at 1. So for that I would suggest a separate wrapper function, which only takes care of such format conversions.

I had a go at a functional approach, creating separate functions for each of the callbacks needed in the process:

 const clone = o => ({...o}); const prefixer = item => arr => [item, ...arr].map(clone); const merger = arr => item => arr.map(prefixer(item)); const extender = group => res => group.flatMap(merger(res)); // The recursive function based on standard array format const cross = (group, ...rest) => group ? extender(group)(cross(...rest)) : [[]]; // For dealing with the unconventional input/output format: const baseOne = (x, i) => [i+1, x]; const generatePermutations = obj => Object.fromEntries(cross(...Object.values(obj).map(Object.values)).map(baseOne)); // Sample data & call const starterObject = { id1: { 1: { value: "A" }, 2: { value: "B" }, 3: { value: "C" }, }, id2: { 1: { value: 10 }, 2: { value: 20 }, }, }; const permutations = generatePermutations(starterObject); console.log(permutations); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM