2015-11-25 13 views
7

Lo sto facendo sul client con Javascript. Voglio trasformare:Come eseguire una funzione di riduzione ricorsiva all'interno di un oggetto?

[ 
    { 
    "id": 10, 
    "name": "Designer", 
    "slug": "designer", 
    "children": [ 
     { 
     "id": 11, 
     "name": "UI/Visual Designer", 
     "slug": "ui-visual-designer", 
     "children": [] 
     }, 
     ... 
    ] 
    }, 
    { 
    "id": 1, 
    "name": "Software Engineer", 
    "slug": "software-engineer", 
    "children": [ 
     { 
     "id": 2, 
     "name": "Back-End Developer", 
     "slug": "back-end-developer", 
     "children": [] 
     }, 
     ... 
    ] 
    }, 
    ... 
] 

in questo:

[ 
    { 
    "id": 10, 
    "text": "Designer" 
    }, 
    { 
    "id": 11, 
    "text": "UI/Visual Designer", 
    }, 
    { 
    "id": 1, 
    "text": "Software Engineer", 
    }, 
    { 
    "id": 2, 
    "text": "Back-End Developer", 
    } 
    ... 
] 

sto praticando con map e reduce Così sto cercando di evitare for loop (prima cosa che ho fatto). Questo è il codice corrente che ho:

var jobNewPage = { 
    ... 
    buildArrayForSelect(array) { 
     "use strict"; 
     return $.extend(true, [], array).reduce(function(total, item) { 
      if (item.slug == 'remote') return total; 

      total.push({ 
       'id' : item.id, 
       'text' : item.name 
      }); 

      let children = item.children; 
      if (children && children.length) { 
       // TODO: We had to call the global context jobNewPage 
       total = total.concat(jobNewPage.buildArrayForSelect(children)); 
      } 
      return total; 
     }, []); 
    }, 
    ... 
} 

Quindi, come potete vedere, ho dovuto chiamare jobNewPage.buildArrayForSelect(children) di farlo in modo ricorsivo. Ho provato a chiamare this.buildArrayForSelect(children) ma il contesto è diverso. Ritengo che questa non sia l'opzione migliore perché non voglio dipendere dal chiamare una variabile globale all'interno di una funzione nell'oggetto. Come posso migliorarlo?

+0

È possibile mantenere 'this' se si utilizza' this.buildArrayForSelect.call (this, children) ' – TbWill4321

risposta

5

Sembra che la tua domanda si riduce a come chiamare ricorsivamente una funzione da dentro se stessa, quando tale funzione viene definita utilizzando un'espressione di funzione e assegnata a una proprietà su un oggetto con ambito superiore.

La semplice risposta è trasformarla in un'espressione di funzione con nome. Tali funzioni sono sono in grado di chiamarsi in modo ricorsivo:

var obj = { 
 
    myMethod: function myName(n) { //function expression has name "myName"... 
 
    console.log(n); 
 
    if (n > 0) 
 
     myName(n-1); //...which we can use inside the function... 
 
    } 
 
} 
 

 
//...and outside we refer to the object's property name 
 
obj.myMethod(5);

Questo approccio, applicato al vostro oggetto e la funzione, apparirebbe come segue:

var jobNewPage = { 
    //give the function expression a name: 
    buildArrayForSelect: function buildArrayForSelect(array) { 
     "use strict"; 
     return $.extend(true, [], array).reduce(function(total, item) { 
      if (item.slug == 'remote') return total; 

      total.push({ 
       'id' : item.id, 
       'text' : item.name 
      }); 

      let children = item.children; 
      if (children && children.length) { 
       //No need to reference object to call the function recursively: 
       total = total.concat(buildArrayForSelect(children)); 
      } 
      return total; 
     }, []); 
    } 
} 
+1

La risposta migliore, tuttavia il codice potrebbe essere basato sul codice OP. Ecco un esempio di codice migliore di questo approccio: http://jsfiddle.net/rtsz4kme/ – dfsq

+0

@dfsq In generale sarei d'accordo e lo farei normalmente, ma in questo caso il codice OP non sembra completo ('...') e questo non è giusto: 'buildArrayForSelect (array) {' così ho deciso che un semplice, completo, esempio per arrivare al nocciolo della domanda sarebbe meglio. –

+0

Il codice OP non solo è completo ma anche abbastanza funzionante, mentre il tuo non è realmente correlato. Ma con l'espressione della funzione con nome è ancora il migliore. – dfsq

0

Si potrebbe fare come un'espressione di funzione immediatamente richiamata. Supponendo che la struttura ad albero si trovi nella variabile "albero";

var tree = []; //your original tree stucture 

var result = (function(input) { 
    var flattened = []; 

    var flattener = function(collection) { 
     collection.forEach(function(item) { 

      flattened.push({id: item.id, text: item.name}); 

      if (item.children.length > 0) { 
       flattener(item.children); 
      } 
     }); 
    } 

    flattener(input); 

    return flattened; 
})(tree); 

console.log(result); 
3

Esempio come utilizzare Array.prototype.reduce in combinazione con Array.prototype.concat in modo ricorsivo.

var data = [{ "id": 10, "name": "Designer", "slug": "designer", "children": [{ "id": 11, "name": "UI/Visual Designer", "slug": "ui-visual-designer", "children": [] }] }, { "id": 1, "name": "Software Engineer", "slug": "software-engineer", "children": [{ "id": 2, "name": "Back-End Developer", "slug": "back-end-developer", "children": [] }] }]; 
 

 
function getAll(array) { 
 
    return array.reduce(function (r, a) { 
 
     r.push({ id: a.id, text: a.name }); 
 
     if (a.children && Array.isArray(a.children)) { 
 
      r = r.concat(getAll(a.children)); 
 
     } 
 
     return r; 
 
    }, []); 
 
} 
 

 
document.write('<pre>' + JSON.stringify(getAll(data), 0, 4) + '</pre>');

+1

Questa è una soluzione molto semplice e diretta. – ftor

0

Prova questo:

Array.prototype.flatten = function() { 
    return this.reduce(function (acc, value) { 
     acc.push(value); 
     acc = acc.concat(value.children.flatten()); 
     return acc; 
    }, []); 
}; 

Array.prototype.extractData = function() { 
    return this.map(function(a) { 
     return (a.slug!='remote')?{'id':a.id,'text':a.name}:false 
    }).filter(function(a) { 
     return (a!=false)?a:false; 
    }); 
}; 

per estrarre i dati:

options=array.flatten().extractData(); 

JsFiddle

Ho appena realizzato che la mia risposta segue un approccio simile alla risposta di James Thorpe (in particolare la soluzione suggerita da dfsq in un commento). Tuttavia, la sua implementazione sembra molto più efficiente della mia.