Entonces, los datos introductorios. Tenemos un grupo de matrices, por ejemplo:
models = [ "audi", "bmw", "toyota", "vw" ];
colors = [ "red", "green", "blue", "yellow", "pink" ];
engines = [ "diesel", "gasoline", "hybrid" ];
transmissions = [ "manual", "auto", "robot" ];
Ahora imaginemos que necesitamos recopilar un conjunto de matrices asociativas (mapa) algo como esto:
variant1 = { "model": "audi", "color": "red", "engine": "diesel", "transmission": "manual" }
variant2 = { "model": "audi", "color": "red", "engine": "diesel", "transmission": "auto" }
variant3 = { "model": "audi", "color": "red", "engine": "diesel", "transmission": "robot" }
variant4 = { "model": "audi", "color": "red", "engine": "gasoline", "transmission": "manual" }
…
variantN = { "model": "vw", "color": "pink", "engine": "hybrid", "transmission": "robot" }
En una forma simplificada, el algoritmo para dicho trabajo se ve así:
for(i1 = 0; i1 < models.length; i1 ++){ //
for(i2 = 0; i2 < colors.length; i2 ++){ //
for(i3 = 0; i3 < engines.length; i3 ++){ //
for(i4 = 0; i4 < transmissions.length; i4 ++){ //
variant = {
"model": models[i1],
"color": colors[i2],
"engine": engines[i3],
"transmission": transmissions[i4],
}
}
}
}
}
Aquellos. de hecho, anidamos cada conjunto dentro de otro conjunto y lo recorremos. Ahora queda por descubrir cómo hacer lo mismo sin estar limitado a un número específico de conjuntos.
Primero, definamos los términos:
Parámetro es el nombre del elemento establecido, por ejemplo, modelo, color, etc.
Un conjunto de elementos de parámetro es una lista asignada a un parámetro (por ejemplo, ["audi", "bmw", "toyota", "vw"])
Un elemento de conjunto es un elemento separado de una lista, por ejemplo, audi, bmw, red, blue, etc.
Conjuntos de resultados : qué deberíamos generar
¿Cómo se verá? Necesitamos una función, cada llamada cambiará en una posición el contador condicional del iterador que controla la iteración de los parámetros (modelo, color, etc.). Dentro de esta función, además de desplazar el contador, iterará sobre los elementos del parámetro (audi, bmw ...; rojo, azul ... etc.). Y dentro de este ciclo anidado, nuestra función se llamará a sí misma de forma recursiva.
El siguiente es un ejemplo funcional en Java con comentarios:
public class App {
public static void main(String[] args) {
Map<String, List<String>> source = Map.of(
"model", Arrays.asList("audy", "bmw", "toyota", "vw"),
"color", Arrays.asList("red", "green", "blue", "yellow", "pink"),
"engine", Arrays.asList("diesel", "gasoline", "hybrid"),
"transmission", Arrays.asList("manual", "auto", "robot")
);
Combinator<String, String> combinator = new Combinator<>(source);
List<Map<String, String>> result = combinator.makeCombinations();
for(Map variant : result){
System.out.println(variant);
}
}
public static class Combinator<K,V> {
//
private Map<K, List<V>> sources;
// .
//ListIterator, .. previous
private ListIterator<K> keysIterator;
//
// - , -
private Map<K, Integer> counter;
//
private List<Map<K,V>> result;
public Combinator(Map<K, List<V>> sources) {
this.sources = sources;
counter = new HashMap<>();
keysIterator = new ArrayList<>(sources.keySet())
.listIterator();
}
//
public List<Map<K,V>> makeCombinations() {
result = new ArrayList<>();
//
loop();
return result;
}
private void loop(){
//,
if(keysIterator.hasNext()){
//
K key = keysIterator.next();
// ( ,
// )
counter.put(key, 0);
//
while(counter.get(key) < sources.get(key).size()){
// loop
loop();
//
counter.put(key, counter.get(key) + 1);
}
// -
keysIterator.previous();
}
else{
// , ..
//
fill();
}
}
//
private void fill() {
Map<K,V> variant = new HashMap<>();
//
for(K key : sources.keySet()){
//
Integer position = counter.get(key);
//
variant.put(key, sources.get(key).get(position));
}
result.add(variant);
}
}
}