web-dev-qa-db-fra.com

Javascript récursif tableau aplatissement

Je fais de l'exercice et j'essaie d'écrire une fonction d'aplatissement de tableau récursif. Le code va ici:

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.Push(flatten(arguments[i]));
        }
        flat.Push(arguments[i]);
    }
    return flat;
}

Le problème est que si j'y passe un tableau ou des tableaux imbriqués, l'erreur "Taille maximale de la pile d'appels dépassée". Qu'est-ce que je fais mal?

11
yxfxmx

Le problème est de savoir comment vous passez le traitement du tableau. Si la valeur est un tableau, vous continuez à l’appeler, ce qui provoque une boucle infinie.

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.Push.apply(flat, flatten.apply(this, arguments[i]));
        } else {
            flat.Push(arguments[i]);
        }
    }
    return flat;
}

Démo: Fiddle

Voici une version plus moderne:

function flatten(items) {
  const flat = [];

  items.forEach(item => {
    if (Array.isArray(item)) {
      flat.Push(...flatten(item));
    } else {
      flat.Push(item);
    }
  });

  return flat;
}
19
Arun P Johny

Une approche haaskellesque ...

function flatArray([x,...xs]){
  return x !== undefined ? [...Array.isArray(x) ? flatArray(x) : [x],...flatArray(xs)]
                         : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
    fa = flatArray(na);
console.log(fa);

3
Redu

Si l'élément est un tableau, nous ajoutons simplement tous les éléments restants à ce tableau

function flatten(array, result) {
  if (array.length === 0) {
    return result
  }
  var head = array[0]
  var rest = array.slice(1)
  if (Array.isArray(head)) {
    return flatten(head.concat(rest), result)
  }
  result.Push(head)
  return flatten(rest, result)
}

console.log(flatten([], []))
console.log(flatten([1], []))
console.log(flatten([1,2,3], []))
console.log(flatten([1,2,[3,4]], []))
console.log(flatten([1,2,[3,[4,5,6]]], []))
console.log(flatten([[1,2,3],[4,5,6]], []))
console.log(flatten([[1,2,3],[[4,5],6,7]], []))
console.log(flatten([[1,2,3],[[4,5],6,[7,8,9]]], []))

2
franziga

Si vous supposez que votre premier argument est un tableau, vous pouvez simplifier les choses.

function flatten(a) {
    return a.reduce((flat, i) => {
      if (Array.isArray(i)) {
        return flat.concat(flatten(i));
      }
      return flat.concat(i);
    }, []);
  }

Si vous voulez aplatir plusieurs tableaux, il suffit de les concaténer avant de les passer. 

2
Tim

Votre code manque une instruction else et l'appel récursif est incorrect (vous passez le même tableau encore et encore au lieu de lui transmettre ses éléments).

Votre fonction pourrait être écrite comme ceci:

function flatten() {
    // variable number of arguments, each argument could be:
    // - array
    //   array items are passed to flatten function as arguments and result is appended to flat array
    // - anything else
    //   pushed to the flat array as-is
    var flat = [],
        i;
    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat = flat.concat(flatten.apply(null, arguments[i]));
        } else {
            flat.Push(arguments[i]);
        }
    }
    return flat;
}

// flatten([[[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]], [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]]);
//            [0, 1, 2,   0, 1, 2,     0, 1, 2,   0, 1, 2,       0, 1, 2,   0, 1, 2,     0, 1, 2,   0, 1, 2]
2
Salman A

Si quelqu'un cherche un tableau aplati d'objets (par exemple, tree), voici un code:

function flatten(items) {
  const flat = [];

  items.forEach(item => {
    flat.Push(item)
    if (Array.isArray(item.children) && item.children.length > 0) {
      flat.Push(...flatten(item.children));
      delete item.children
    }
    delete item.children
  });

  return flat;
}

var test = [
	{children: [
      {children: [], title: '2'}
      ],
  title: '1'},
	{children: [
      {children: [], title: '4'},
      {children: [], title: '5'}
      ],
  title: '3'}
]

console.log(flatten(test))

1
ulou

Les autres réponses indiquaient déjà la source du dysfonctionnement du code du PO. En écrivant un code plus descriptif, le problème se résume littéralement à une "détection de matrice/-reduce/-concat-récursivité" ...

(function (Array, Object) {


//"use strict";


  var
    array_prototype       = Array.prototype,

    array_prototype_slice = array_prototype.slice,
    expose_internal_class = Object.prototype.toString,


    isArguments = function (type) {
      return !!type && (/^\[object\s+Arguments\]$/).test(expose_internal_class.call(type));
    },
    isArray     = function (type) {
      return !!type && (/^\[object\s+Array\]$/).test(expose_internal_class.call(type));
    },

    array_from  = ((typeof Array.from == "function") && Array.from) || function (listAlike) {
      return array_prototype_slice.call(listAlike);
    },


    array_flatten = function flatten (list) {
      list = (isArguments(list) && array_from(list)) || list;

      if (isArray(list)) {
        list = list.reduce(function (collector, Elm) {

          return collector.concat(flatten(Elm));

        }, []);
      }
      return list;
    }
  ;


  array_prototype.flatten = function () {
    return array_flatten(this);
  };


}(Array, Object));

emprunter du code à l’une des autres réponses comme preuve de concept ...

console.log([
  [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]],
  [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]
].flatten());
//[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, ..., ..., ..., 0, 1, 2]
0
Peter Seliger

Cela devrait marcher

function flatten() {
    var flat = [
    ];
    for (var i = 0; i < arguments.length; i++) {
        flat = flat.concat(arguments[i]);
    }
    var removeIndex = [
    ];
    for (var i = flat.length - 1; i >= 0; i--) {
        if (flat[i] instanceof Array) {
            flat = flat.concat(flatten(flat[i]));
            removeIndex.Push(i);
        }
    }
    for (var i = 0; i < removeIndex.length; i++) {
        flat.splice(removeIndex - i, 1);
    }
    return flat;
}
0
Hopesun

Ceci est une solution JavaScript Vanille à ce problème

var _items = {'keyOne': 'valueOne', 'keyTwo': 'valueTwo', 'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]};

// another example
// _items = ['valueOne', 'valueTwo', {'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]}];

// another example
/*_items = {"data": [{
    "rating": "0",
    "title": "The Killing Kind",
    "author": "John Connolly",
    "type": "Book",
    "asin": "0340771224",
    "tags": "",
    "review": "i still haven't had time to read this one..."
}, {
    "rating": "0",
    "title": "The Third Secret",
    "author": "Steve Berry",
    "type": "Book",
    "asin": "0340899263",
    "tags": "",
    "review": "need to find time to read this book"
}]};*/

function flatten() {
    var results = [],
        arrayFlatten;

    arrayFlatten = function arrayFlattenClosure(items) {
        var key;
        
        for (key in items) {
            if ('object' === typeof items[key]) {
                arrayFlatten(items[key]);
            } else {
                results.Push(items[key]);
            }
        }
    };

    arrayFlatten(_items);
    
    return results;
}

console.log(flatten());

0
CodeMaxter