¿Cómo obtener todas las combinaciones posibles de múltiples listas con diferentes índices solo en javascript? [duplicar]

Resuelto Frinch87 asked hace 9 meses • 0 respuestas

Supongamos que tengo las siguientes listas:

const list1 = [1,2,3,4,5]
const list2 = [6,7,8,9,10]
const list3 = [11,12,13,14,15]
const list4 = [16,17,18,19,20]

¿Cómo obtengo todas las combinaciones posibles pero solo con índices diferentes?

Entonces, por ejemplo, el resultado [1,6,11,16]no funcionaría.

Necesitaría todas las combinaciones como:

[1, 2, 3, 4, 5],
[1, 2, 3, 4, 10],
[1, 2, 3, 9, 5].
[1, 2, 3, 9, 10],
[1, 2, 8, 4, 5],
[1, 2, 8, 9, 5],
[1, 2, 8, 4, 10],
[1, 2, 8, 9, 10],
...
[6, 2, 3, 4, 5], 
[6, 2, 13, 14, 20]

etcétera...

Por lo tanto, el índice de cada lista solo debe usarse una vez. No puede utilizar el valor de lista1[0] y de otro índice de lista[0].

También necesitaría saber de dónde proviene cada elemento de las listas devueltas.

Frinch87 avatar Feb 16 '24 14:02 Frinch87
Aceptado

Esto parece resolver lo que quieres.

// Using a generator because this can yield a lot of values. Making an array
// from a generator is easy, and generator results can be fetched one after the other,
// reducing memory load.
function* generateIndexCombinations(...lists) {
  // Fetch how many cell we needs in our results.
  const size = Math.max(...lists.map(l => l.length));

  // Using a helper so we can add the index parameter. We could also do by
  // slicing the lists, but this would have much larger memory footprint.
  function* doGenerate(lists, index, acc) {
    // Recursion end case.
    if (index >= size) {
      yield acc;
      return;
    }

    for (const list of lists) {
      if (list.length > index) {
        // For each remaining list, create a copy of acc including the index value.
        const newAcc = [...acc, list[index]];
        // Generate the combinations of the tail to be added in acc and re-yield them.
        yield* doGenerate(lists, index + 1, newAcc)
      }
    }
  }
  yield* doGenerate(lists, 0, []);
}



const list1 = [1, 2, 3, 4, 5];
const list2 = [6, 7, 8, 9, 10];
const list3 = [11, 12, 13, 14, 15];
const list4 = [16, 17, 18, 19, 20];

const table = document.querySelector('table');

// Option 1: get each combination one after the other.
for(let c of generateIndexCombinations(list1, list2, list3, list4)) {
  const tr = document.createElement('tr');
  for (let v of c) {
    const td = document.createElement('td');
    td.textContent = v;
    tr.appendChild(td);
  }
  table.appendChild(tr);
}

// Option 2: Store all combinations in an array.
console.log([...generateIndexCombinations(list1, list2, list3, list4)]);
table td {
  padding: 0 0.25em;
}
<table>
</table>
Expandir fragmento

Editar : si también necesita asociar cada valor con la lista de la que proviene, usaría una función de mapeo de la siguiente manera:

// Using a generator because this can yield a lot of values. Making an array
// from a generator is easy, and generator results can be fetched one after the other,
// reducing memory load.
function* generateIndexCombinations(lists, mapper = x => x) {
  // Fetch how many cell we needs in our results.
  const size = Math.max(...lists.map(l => l.length));

  // Using a helper so we can add the index parameter. We could also do by
  // slicing the lists, but this would have much larger memory footprint.
  function* doGenerate(lists, valueIndex, acc) {
    // Recursion end case.
    if (valueIndex >= size) {
      yield acc;
      return;
    }

    for (let listIndex = 0; listIndex < lists.length; listIndex++) {
      const list = lists[listIndex];
      if (list.length > valueIndex) {
        const val = mapper(list[valueIndex], listIndex, valueIndex, lists);
        // For each remaining list, create a copy of acc including the index value.
        const newAcc = [...acc, val];
        // Generate the combinations of the tail to be added in acc and re-yield them.
        yield* doGenerate(lists, valueIndex + 1, newAcc)
      }
    }
  }
  yield* doGenerate(lists, 0, []);
}



const list1 = [1, 2, 3, 4, 5];
const list2 = [6, 7, 8, 9, 10];
const list3 = [11, 12, 13, 14, 15];
const list4 = [16, 17, 18, 19, 20];

const table = document.querySelector('table');

// Example 1: without mapper, get each combination one after the other.
for (let c of generateIndexCombinations([list1, list2, list3, list4])) {
  const tr = document.createElement('tr');
  for (let v of c) {
    const td = document.createElement('td');
    td.textContent = v;
    tr.appendChild(td);
  }
  table.appendChild(tr);
}

// Example 2: Store all combinations in an array, using the new optional mapper to map 
// each value to another.
function listCombinationValueMapper(value, listIndex, valueIndex, lists) {
  return {
    value,
    listIndex,
    valueIndex,
    // The source list.
    source: lists[listIndex]
  };
}
const combinations = [...generateIndexCombinations(
  [list1, list2, list3, list4],
  listCombinationValueMapper
)];
console.log(JSON.stringify(combinations));
table td {
  padding: 0 0.25em;
}
<table>
</table>
Expandir fragmento

Quentin Roy avatar Feb 16 '2024 08:02 Quentin Roy