0

I am trying to create an object array that will contain objects that represent every scenario of combination possibilities that are provided by three string arrays. Each object in my Object Array (Collection) should contain the properties {format: '', iconType: '', state: ''} where each property can be a sting or a null value.

So in my code I have 3 string arrays... like so:

    const buttonFormats = ['compact', 'regular', 'fluid'];
    const stateParams = [
       null,
      'secondary',
      '2',
      'tertiary',
      '3',
      'warning',
      '!',
      'danger',
      '!!!',
      'danger-secondary',
      '2!!!',
      'primary-line',
      '1l',
      'secondary-line',
      '2l',
      'tertiary-line',
      '3l',
      'warning-line',
      '!l',
      'danger-line',
      '!!!l'
    ];
    const iconTypes = [null, 'add', 'edit'];
    const allCombinations = [];

My plan is to have three nested loops where we take the current value of one loop and combine it with the values of the other loops, like so...

public makeButtonStates(): any[] {
    const buttonFormats = ['compact', 'regular', 'fluid'];
    const stateParams = [
       null,
      'secondary',
      '2',
      'tertiary',
      '3',
      'warning',
      '!',
      'danger',
      '!!!',
      'danger-secondary',
      '2!!!',
      'primary-line',
      '1l',
      'secondary-line',
      '2l',
      'tertiary-line',
      '3l',
      'warning-line',
      '!l',
      'danger-line',
      '!!!l'
    ];
    const iconTypes = [null, 'add', 'edit']; // these are the available icons
    const allCombinations = [];

    buttonFormats.forEach((format) => {
      const currentCombinaton: any = {};
      currentCombinaton.format = format;

      iconTypes.forEach((icon) => {
        currentCombinaton.iconType = icon;

        stateParams.forEach((state) => {
          currentCombinaton.state = state;
          console.log(currentCombinaton);
          allCombinations.push(currentCombinaton);
        });
      });
    });

    return allCombinations;
  }

public ngOnInit(): void {
    this.buttonStates = this.makeButtonStates();
    console.log(this.buttonStates);
  }

This isn't working as although when I output using console.log() the current combination of values what is written to the console is what I would expect, however when I write the outcome of my nested loop function, the value of this.buttonStates or allCombinations in the all the objects in the object array have the same value for the iconType and state properties, these being the last times in the string arrays ´{iconType: "edit", state: "!!!l"}` This screen shot of the developer console gives an idea of the output...

enter image description here

Obviously my implementation is wrong, should I use .map or .filter or .reduce instead and why is my output array only taking the last values of the stateParams and iconType array?

Many thanks in advance if you can help me spot this.

3 Answers 3

1

Without nested loops, you could take a combination algorithm for the values and apply an object structure later.

const
    buttonFormats = ['compact', 'regular', 'fluid'],
    iconTypes = [null, 'add', 'edit'],
    stateParams = [ null, 'secondary', '2', 'tertiary', '3', 'warning', '!', 'danger', '!!!', 'danger-secondary', '2!!!', 'primary-line', '1l', 'secondary-line', '2l', 'tertiary-line', '3l', 'warning-line', '!l', 'danger-line', '!!!l'],
    result = [buttonFormats, iconTypes, stateParams]
        .reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []))
        .map(([format, iconType, state]) => ({ format, iconType, state }));


console.log(result.length);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

Comments

0

You can run a nested set of loops, I'm not sure if a more efficient way exists:

const buttonFormats = ['compact', 'regular', 'fluid'];
const iconTypes = [null, 'add', 'edit'];
const states = [null, 'secondary', '2', 'tertiary', '3', 'warning', '!', 'danger', '!!!', 'danger-secondary', '2!!!', 'primary-line', '1l', 'secondary-line', '2l', 'tertiary-line', '3l', 'warning-line', '!l', 'danger-line', '!!!l'];

const allCombinations = [];

buttonFormats.forEach((bf) => {
    iconTypes.forEach((it) => {
        states.forEach((s) => {
            allCombinations.push({
                format: bf,
                icon: it,
                state: s
            });
        });
    });
});

console.log(allCombinations);

The key difference is about when the object is defined. i.e. if I define a const in an outer loop, or outside of the loop, I am re-using the reference to the same object (which means I end up updating the same object in the nested parts of the code). By creating a new object in the innermost loop I avoid this.

Comments

0

This is called a cartesian product. Usually it's written recursively, based on the following observation:

to obtain a cartesian product Cn of N arrays, compute the product Cn-1 for N-1 arrays, and merge each element of the first array with each element in Cn-1

Here's an example implementation in the functional style

flatMap     = (xs, f) => [].concat(...xs.map(f))
prepend     = x => ys => [x].concat(ys)
prependEach = (xs, yss) => flatMap(xs, x => yss.map(prepend(x)))
product     = (xs, ...rest) => xs ? prependEach(xs, product(...rest)) : [[]]


// demo

a = [1,2,3]
b = ['A', 'B', 'C']
c = ['foo', 'bar']


g = product(a, b, c)
g.forEach(x => console.log(x.join('-')))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.