2012-09-10 3 views
41

Immagina di avere una struttura di array nidificata.Qual è il underscore.js equivalente all'operatore SelectMany di LINQ?

var nested = [ [1], [2], [3] ]; 

Utilizzando underscore.js, come vorrei produrre un allineamento appiattita?

In C# si dovrebbe usare Enumerable.SelectMany come questo:

var flattened = nested.SelectMany(item => item); 

Si noti che il lambda in questo caso seleziona direttamente la voce nidificato, ma avrebbe potuto essere qualsiasi espressione arbitraria.

In jQuery, è possibile utilizzare solo:

var flattened = $.map(nested, function(item) { return item; }); 

Tuttavia questo approccio non funziona con la funzione di sottolineatura map.

Quindi, come posso ottenere l'array appiattito [1, 2, 3] utilizzando underscore.js?

+3

uso _.flatten? – yngccc

+0

si potrebbe anche scrivere: _.map (nested, function (item) {return item [0];}) – Darragh

+0

@Darragh, che funzionerebbe per il mio esempio specifico, ma non quando gli array figlio contengono più elementi. –

risposta

35
var nested = [ [1], [2], [3] ]; 
var flattened = _.flatten(nested); 

Heres un fiddle

+0

Bello e facile! Perché non l'ho visto nella loro documentazione?:) –

+6

Avviso: pass shallow = true, se si desidera un solo livello di appiattimento (come SelectMany): '_.flatten (nested, true)' –

+2

['flatten'] (https://lodash.com/docs #flatten) sembra eseguire solo un livello di default ora; hanno aggiunto 'flattenDeep' e' flattenDepth'. – mpen

37

Se si dispone di una gamma leggermente più complicato, dire quello proveniente da JSON, è possibile usufruire del metodo pluck pure, estraendo la proprietà specifica che ti interessa, simile a parents.SelectMany(parent => parent.Items);

// underscore version 
var allitems = _.flatten(_.pluck(parents, 'items')); 

allitems è ora la matrice di tutti i sottoelementi da parte dei genitori, [a,b,c,d].

E uno JSFiddle che mostra la stessa cosa.


Oppure, se si utilizza lodash si può fare la stessa cosa utilizzando la funzione _.flatMap che è disponibile a partire dalla versione 4. Cred a Noel per segnalarlo nei commenti.

var parents = [ 
 
    { name: 'hello', items: ['a', 'b'] }, 
 
    { name: 'world', items: ['c', 'd'] } 
 
]; 
 

 

 
// version 1 of lodash, straight up 
 
var allitems = _.flatMap(parents, 'items'); 
 
logIt('straight', allitems); 
 

 
// or by wrapping the collection first 
 
var allitems = _(parents) 
 
    .flatMap('items') 
 
    .value(); 
 
logIt('wrapped', allitems); 
 

 
// this basically does _(parents).map('items').flatten().value(); 
 

 
function logIt(wat, value) { 
 
    document.getElementById('result').innerHTML += wat + ':' + JSON.stringify(value) + '\r\n<br/>'; 
 
}
<script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"></script> 
 
<div id="result"></div>

+1

Sebbene non sia disponibile in underscore, con lodash, tu potrebbe semplificare l'uso di 'map' e' flatten' per usare solo 'flatMap', come questo:' _.flatMap (parents, "items") '). – Noel

+0

Grazie a @Noel, lo aggiungerò alla mia risposta. Non sapevo di quello quando ho scritto la mia risposta – Patrick

2

non riuscivo a trovare alcun metodo in lodash che funzionano abbastanza come SelectMany, così ho creato uno utilizzando JS puri:

Array.prototype.selectMany = function(fn) { 
    return Array.prototype.concat(...this.map(fn)); 
}; 

Boom.

> console.log([{a:[1,2],b:'x'},{a:[3,4],b:'y'}].selectMany(o => o.a)); 
[ 1, 2, 3, 4 ] 
+0

Davvero? Ho incluso un campione funzionante nella mia risposta .. Cosa c'è di sbagliato in questa soluzione? – Patrick

+0

@Patrick L'ordine delle operazioni. Diventa confuso quando c'è il nidificazione: 'a.selectMany (b => b.c.selectMany (d => d.e))'. Prova a riscrivere quello usando la tua soluzione. – mpen

3

Possiamo anche fare Patrick's solution in un mixin in modo che diventi chainable:

_.mixin({ 
    selectMany: function(collection, iteratee=_.identity) { 
     return _.flatten(_.map(collection, iteratee)); 
    } 
}); 

Esempi:

let sample = [{a:[1,2],b:'x'},{a:[3,4],b:'y'}]; 

console.log(_.selectMany(sample, 'a')); // [ 1, 2, 3, 4 ] 
console.log(_.chain(sample).selectMany(o => o.a).filter(a => a % 2 === 0).map(a => a * 3).value()); // [ 6, 12 ]