0

I'm trying to write a program that generates every single item based on my JSON structure and giving each combination an individual number. I found a function on here that so far does its job listing out every combination for me, however I cannot decipher the code to the point I actually even understand what it does. It gives me all the items, listen like Key : Value but honestly I have no idea what part of the code does what and I cannot access it in order to build in my giving them unique numbers. This is the code that I found on here (lost the thread to it):

    function getCartesian(object) {
        return Object.entries(object).reduce((r, [k, v]) => {
            var temp = [];
            r.forEach(s =>
                (Array.isArray(v) ? v : [v]).forEach(w =>
                    (w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
                        temp.push(Object.assign({}, s, { [k]: x }))
                    )
                )
            );
            return temp;
        }, [{}]);
    }
    
    var input = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } },
        cartesian = { eachBook: getCartesian(input.bookSet) };

It's just written in a too advanced syntax for me to remotely understand where I have to insert myself to make any calculations. I guess what I'm asking for would be either an explanation or a somewhat more understandable and modifyable code. I definitely need to run through all elements like this is doing and the output looks great from what I could tell so far, I just need to somehow calculate the keys and have an output of a number derived of each object's elements' combined keys. An example would be book 243 for title 2, author 4 and publisher 3. I hope anyone can make sense of this. Thanks a lot!

EDIT: Included my own data and desired output. The combinations I displayed don't need to make sense.

var Product = {
    json:  { Product : {
        assortment: [
        {
            name: "Yoghurt",
            Flavor: ["natural", "honey", "stracciatella"],
            Kind: ["greek", "soy"],            
        },
        {
            name: "Sauce",
        },
        {
            name: "Milk Drink",             
        }
        ],
        Brand: ["Oatly", "Dannon"],
        Containment: ["Cup", "Jar"]
}}};

My output I'd like to generate the combinations of all of those and ultimately calculate the numbers on the right in the following screenshot

3 Answers3

0

Given C_1 and C_2 two sets

The cartesian product of C_1 and C_2

is given by C_1 x C_2 = {(c_1_i,c_2_j) for c_1_i in C_1, c_2_j in C_2}

You can build C_1 x C_2 x C_3 by considering (C_1 x C_2) (that you calculated before) and "adjoining" each elem of C_3 to a tuple of C_1 x C_2

And so forth

const cartesianProduct = (C, D) => {
  const res = []
  C.forEach(c => {
    D.forEach(d => {
      // in case the tuple contains only one element (the initialization)
      // make the elmeent into a tuple
      const tuple = Array.isArray(c) ? c : [c]
      res.push([...tuple,d])
    })
  })
  return res
}


const nCartesianProduct = (Cs_n) => {
  // we adjoin each elem of C_i and we "grow"
  return Cs_n.reduce((res, C_i) => cartesianProduct(res, C_i))
}

console.log(nCartesianProduct([['b1', 'b2', 'b3'], ['t1', 't2'], ['a'], ['p']]))
grodzi
  • 5,633
  • 1
  • 15
  • 15
0

Here is my attempt to lay in a simple terms:

Lets assume an example of

const sets = [ [1], [1,2], [1,2,3] ]

Possible combinations may be logged as following:

1 1 1      1 2 1  
1 1 2  ->  1 2 2
1 1 3      1 2 3

Lets think of it as a clock, where last row will increase the value of previous row, once it reaches its maximum. In another words: lets increase i position of the last row and when over the limit -> drop it to zero and increase sibling instead, where if sibling is over the top -> repeat.

Consider the following code:

let sets = [[1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5] ];
let state = sets.map( () => 0 );

console.log(sets, state);

function handleIncreament(i){
    
    if( state[i] >= sets[i].length){

            if(i-1 < 0) {
                console.log('end of the row'); 
                return false;
            }

            state[i] = 0;
            state[i-1] += 1;
        
        return handleIncreament(i-1);
    }
    else {
        return true;
    }
}

while( handleIncreament(state.length - 1) ){
    console.log( state );

    state[state.length - 1]++;
}

Above will log as follows:

(4) [Array(2), Array(3), Array(4), Array(5)] (4) [0, 0, 0, 0]
(4) [0, 0, 0, 0]
(4) [0, 0, 0, 1]
(4) [0, 0, 0, 2]
(4) [0, 0, 0, 3]
(4) [0, 0, 0, 4]
(4) [0, 0, 1, 0]
(4) [0, 0, 1, 1]
(4) [0, 0, 1, 2]
...
(4) [1, 2, 3, 4]
end of the row
4

With that lets apply it to your example:

const test = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } };
sets = Object.values(test.bookSet);
state = sets.map( () => 0 );

console.log(sets, state);

const matches = [];

while( handleIncreament(state.length - 1) ){
    
    const match = sets[0][state[0]] + ' ' + sets[1][state[1]] + ' ' + sets[2][state[2]] + ' ' + sets[3][state[3]];
    
    matches.push( match );

    state[state.length - 1]++

}

console.log(matches);

And expect to get the following:

["book1 title1 author1 publisher1", "book1 title2 author1 publisher1", "book2 title1 author1 publisher1", "book2 title2 author1 publisher1", "book3 title1 author1 publisher1", "book3 title2 author1 publisher1"]
0

You could take the above data without superfluous parts and simplify the result by creating a flat array of the nested properties.

The numbers of the result picture are not incuded, because of the missing relation of each value to the given data set.

function getCartesian(object) {
    return Object.entries(object).reduce((r, [k, v]) => {
        var temp = [];
        r.forEach(s =>
            (Array.isArray(v) ? v : [v]).forEach(w =>
                (w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
                    temp.push(Object.assign({}, s, { [k]: x }))
                )
            )
        );
        return temp;
    }, [{}]);
}

var data = {
        assortment: [
            {
                name: "Yoghurt",
                Flavor: ["natural", "honey", "stracciatella"],
                Kind: ["greek", "soy"],
            },
            {
                name: "Sauce",
            },
            {
                name: "Milk Drink",
            }
        ],
        Brand: ["Oatly", "Dannon"],
        Containment: ["Cup", "Jar"]
    },
    result = getCartesian(data)
        .map(({ assortment: { name, Flavor = '', Kind = '' }, d = '', Brand, f = '', Containment, h = '', i = '', j = '' }) =>
            [name, Flavor, Kind, d, Brand, f, Containment, h, i, j]);

console.log(result.length);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392