2
votes

Aplatir les tableaux imbriqués en utilisant la récursivité (et sans utiliser de boucles)

Ma logique du problème, en utilisant ce qui suit comme entrée.

//Defining original input variable
var input = [['A','B'],1,2,3,['C','D']]

function flat(array){
    var result = []
    var firstElement = array[0]

//CHECK IF FIRST ELEMENT IS ARRAY OR NOT
    if(Array.isArray(firstElement)){
    return flat(firstElement)  
    } 


//IF ELEMENT NOT ARRAY, PUSH ELEMENT TO RESULT
    else{result.push(firstElement)
    array.shift() //removing child element
    return (flat(array)) //call function on same array
    }

if(array.length===0){return result}

}
  1. Vérifiez le premier élément pour voir s'il s'agit d'un tableau ou s'il n'utilise pas Array.isArray (entrée)
  2. Si le premier élément est un tableau, appelez la fonction, premier élément ['A,' B '] comme argument.
  3. Le premier élément du tableau imbriqué est 'A' qui n'est pas un tableau, alors poussez cet élément dans un tableau de résultats et décalez cet élément. Répétez l'appel de fonction.

En essayant d'aplatir des tableaux imbriqués en utilisant la récursivité, ma variable d'entrée à la fonction continue d'être réaffectée, m'empêchant de rappeler la fonction en utilisant le tableau d'origine. Comment éviter que la variable d'entrée d'origine ne soit réaffectée?

Je comprends que ce n'est pas la solution complète, mais je suis bloqué lorsque je déplace le premier élément hors du tableau imbriqué.

J'ai parcouru étape par étape avec ceci fonction, mais il doit y avoir quelque chose qui me manque, une autre paire d'yeux aiderait grandement.

J'ai également utilisé mon outil de développement Chrome, définissez des points d'arrêt pour surveiller la fonction étape par étape.

var input = [['A','B'],1,2,3,['C','D']]

Première itération: firstElement = ['A', 'B'], Array.isArray (firstElement) serait vrai, donc appelez flat (firstElement)

Deuxième itération: firstElement = 'A', Array.isArray (firstElement) est false, donc nous 1. sauter pour pousser cet élément dans le résultat 2. supprimez 'A' en utilisant array.shift () 3. Appelez flat (array), où array est maintenant ['B']

Troisième itération: firstElement = 'B', Array.isArray (firstElement) est faux 1. Sautez vers le bas pour pousser cet élément dans le résultat, le résultat est maintenant seulement ['B'] puisque j'ai réinitialisé le résultat lorsque j'ai rappelé la fonction. 2. supprimez 'B' en utilisant array.shift (), le tableau est maintenant vide, -> [] 3. Comment puis-je sortir et utiliser flat () sur le tableau d'entrée d'origine?


4 commentaires

Pourquoi? Juste pourquoi? C'est un exercice purement didactique, vous n'aurez jamais besoin de quelque chose comme ça dans la vraie vie


@ CristianTraìna C'est un excellent exercice pour comprendre la récursivité.


@ CristianTraìna - Heureux de savoir que je n'utiliserai pas ça dans la vraie vie haha. J'aurais pu facilement le faire en utilisant des boucles, mais j'essaie de rafraîchir mes compétences en récursivité. C'est encore fragile. Ce n'était qu'un des problèmes de pratique.


C’est un bon exercice. Et utile dans la vraie vie dès que les tableaux imbriqués peuvent également inclure plus de tableaux imbriqués comme un arbre.


3 Réponses :


0
votes

Si vous voulez éviter les boucles, et que je considère concat ing / spreading arrays as loops, vous devez passer le tableau de résultats à votre fonction.

const input = [['A', 'B'], 1, 2, 3, ['C', 'D']]

// Start the function with an empty result array.
function flat(array, result = []) {
  if (!array.length)
    return result

  // Extract first element.
  const first = array.shift()

  // Call the function with the array element and result array.
  if (Array.isArray(first))
    flat(first, result)
  // Or add the non array element.
  else
    result.push(first)

  // Call the function with the rest of the array and result array.
  flat(array, result)
  return result

}

console.log(flat(input))


1 commentaires

@destroyer - Merci pour celui-ci. J'étais réticent à utiliser un deuxième argument car j'insistais pour essayer de comprendre ma première tentative. Cependant, j'ai pu saisir votre solution de manière beaucoup plus intuitive. Appréciez cette solution!



1
votes

Votre code ne prend pas en compte les éléments suivants si le premier élément est un tableau. La solution ci-dessous utilise array.concat (...) pour combiner à la fois le résultat de la récursivité (en descendant l'arborescence), mais aussi pour combiner les résultats du traitement du reste de la liste (dans le même niveau). Visualiser le problème sous forme d'arbre, aide souvent avec les récursions IMO:

var input = [['A',['B', 'C']],1,2,3,['C','D']]
function flat(array, depth) {
    var result = []
    if (array.length == 0) return result;

    if (Array.isArray(array[0])) {
        result = result.concat(flat(array[0], depth + 1));
    } else {
        result.push(array[0]);
    }
    var res1 = flat(array.slice(1), depth);
    console.log("Depth: " + depth + " | Concatenating: [" + result + "]  with: [" + res1 + "]");
    result = result.concat(res1)

    return result;
}

console.log(flat(input, 0));

Alors peut-être est-il plus clair ici, que nous devons à la fois concaténer le résultat de la récursion et le résultat d'un pas "à droite (encore une fois récursivité) qui serait autrement une boucle itérant le tableau.

function flat(array) {
    var result = []

    for (var i = 0; i < array.length; i++) {
        if (Array.isArray(array[i])) {
            result = result.concat(flat(array[i]));
        } else {
            result.push(array[i]);
        }
    }
    return result;
}

Ceci est un peu analogue à une version avec des boucles:

var input = [['A',['B', 'C']],1,2,3,['C','D']]

function flat(array) {
    var result = []
    if (array.length == 0) return result;
  
    if (Array.isArray(array[0])) {
        result = result.concat(flat(array[0]));    // Step down
    } else {
        result.push(array[0]);
    }
    result = result.concat(flat(array.slice(1)))   // Step right

    return result;
}

console.log(flat(input));
// ["A", "B", "C", 1, 2, 3, "C", "D"]

EDIT: à des fins de débogage, vous pouvez suivre la profondeur pour aider à obtenir un aperçu de ce se produit là où:

 [] 1 2 3 []
 |         |
A []      C D
   |
  B C


5 commentaires

j'ai une question de clarification mais d'abord, merci beaucoup, votre première solution que j'essayais d'écrire. Première itération l'avant-dernière ligne flat (array.slice (1)) , ici array est l'entrée d'origine var input = [ ['A', ['B', 'C']], 1,2,3, ['C', 'D']] Deuxième itération , flat (array.slice (1)) est ['A', ['B', 'C']] , puis nous découpons 'A' Troisième itération , flat (array.slice (1)) est ['B', 'C'] , jusqu'à array.length === 0 < / code> Comment sommes-nous sortis des tableaux imbriqués au point où lorsque nous appelons flat (array.slice (1)) , le array fait en fait référence à notre entrée originale?


Cela a quelque chose à voir avec la pile d'appels renvoyant les valeurs de «résultat», n'est-ce pas? Toutes mes excuses pour le long suivi. J'ai utilisé le visualiseur Pythontutor pour cela, je pense que je vais juste devoir le parcourir plus lentement.


@MaxHsu La "sortie" se fait implicitement par la pile d'appels de fonction. Prenez un morceau de papier et essayez de suivre les choses.


@MaxHsu Oh, et .slice (1) vous laisse avec le même tableau que le vôtre après avoir appelé .shift () dessus.


@MaxHsu J'ai ajouté un extrait de code qui fournit un peu plus d'informations. array.slice (1) renvoie array à partir de l'index 1 (donc en excluant le premier élément). Dans la première itération, cela signifie 1,2,3, ['C', 'D']] - qui correspond au "reste" du niveau supérieur dans l'arborescence de mon message. Donc finalement, la récursion revient à l'élément en haut à gauche et est retournée.



0
votes

Voici ma réponse si vous utilisez JavaScript

Vous pouvez utiliser le code d'une ligne ci-dessous pour aplatir le tableau imbriqué de n niveaux

function flatDeep(arr, d = 1) {
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
                : arr.slice();
};

Ou utilisez cette approche en utilisant la réduction et concattre

let flattendArray = input.flat(Infinity);

Référez ce lien


0 commentaires