2010-10-15 15 views
22

Utilizzando Underscore.js, posso scrivere quanto segue, che ritorna 42:Underscore.js: come funzioni personalizzate a catena

_([42, 43]).chain() 
    .first() 
    .value() 

ho funzione personalizzata, non fa parte della Underscore.js chiamato double():

function double(value) { return value * 2; }; 

Vorrei poter chiamare questa funzione in una catena Underscore, come se fosse parte di Underscore. Vorrei scrivere la seguente, che mi piacerebbe tornare 84:

_([42, 43]).chain() 
    .first() 
    .double() 
    .value() 

Questo non può funzionare poiché sottolineatura non definisce double(). Potrei usare tap() come in:

_([42, 43]).chain() 
    .first() 
    .tap(double) 
    .value() 

Questo è valido, ma tap applica la funzione al suo argomento e restituisce l'argomento, non il risultato della funzione. Quindi mi sembra che avrei bisogno di una sorta di tap che restituisca il risultato della funzione applicata al suo argomento. C'è qualcosa di simile in Underscore.js? Mi manca qualcosa di terribilmente ovvio?

+4

Tieni presente che 'double' è una [* parola riservata in futuro *] (http://bclary.com/2004/11/07/#a-7.5.3) e che le implementazioni possono lanciare un 'Errore Syntax' se è usato come un Identifier. – CMS

+0

Alessandro, hai già capito che ti ho dato l'unica risposta corretta a questa domanda? Ciò che ho esposto non inquina affatto lo spazio dei nomi di sottolineatura. La libreria underscore.js ha anticipato ciò che hai chiesto e incorporato in una funzione esattamente per quello. –

+0

Underscore ora ha sia "tap" (esegue una funzione per modificare ciascun elemento) che "map" (esegue una funzione che restituisce un nuovo elemento). – CMircea

risposta

23

non trovare un tap che restituisce i rendimenti di valore dalla funzione viene eseguito, definisco una che posso take e aggiungere al _:

_.mixin({take: function(obj, interceptor) { 
    return interceptor(obj); 
}}); 

Poi assumendo che ho:

function double(value) { return value * 2; }; 

mi può scrivere:

_([42, 43]).chain() 
    .first()    // 42 
    .take(double)  // Applies double to 42 
    .value()    // 84 

È possibile guardare take come map su oggetti, anziché elenchi. Vuoi sperimentare con questo? Vedi questo example on jsFiddle.

+1

'apply' potrebbe essere un nome migliore –

+4

@Gabe, ho pensato di chiamarlo' apply', ma il nome è già definito sulle funzioni, e ha una semantica diversa: 'f.apply (obj, argArray)' invece di 'obj.take (f, argArray)'. Non pensi che questo possa essere confusione? (Chiedo onestamente di non essermi convinto neanche io.) – avernet

+1

Per coloro che cercano, 'tap' fa ora parte del nucleo di underscore. –

5

in modo da avere una funzione personalizzata:

function double(value) { return value * 2; } 

È possibile utilizzare mixin per estendere la sottolineatura con esso:

_.mixin({ double:double }); 

Ora è possibile chiamare la funzione dall'oggetto Underscore _:

_.double(42); // 84 

e dall'oggetto spostato restituito da chain:

_([42, 43]).chain() 
    .first() 
    .double() // double made it onto the wrapped object too 
    .value(); // 84 
+0

sì, questo è un modo per farlo. Qualcosa che non mi piace di questo è che inquina '_'. Cioè questo non è fattibile se usi il concatenamento ovunque nel tuo codice, e quindi hai molte di quelle funzioni. – avernet

+0

Ho suggerito un'alternativa che mi richiede di aggiungere una sola funzione a '_' - vedere la risposta alla mia domanda. Sperimenterò con questo e vedrò come funziona in pratica. – avernet

-1

map funziona per questo?

_([42, 43]).chain() 
    .first() 
    .map(double) 
    .value() 

modificare

dalla documentazione, sembra che map funzionerebbe solo se lo si inserisce prima della chiamata a first:

_([42, 43]).chain() 
    .map(double) 
    .first() 
    .value() 
+0

right, 'map' non lo taglia perché funziona solo in lista. Qui ho bisogno di una sorta di 'mappa' che funziona su singoli" oggetti ". – avernet

+0

@avernet si potrebbe semplicemente prefisso e mappa postfix con prima. – arcseldon

-1

Utilizzando compose è un altro modo trattare con il situazione, ma penso che l'aggiunta di una funzione come takeas I suggested earlier è una soluzione migliore. Eppure, ecco come il codice sarà simile con compose:

function double(value) { return value * 2; }; 

_.compose(
    double, 
    _.first, 
    _.bind(_.identity, _, [42, 43]) 
)(); 

Il valore iniziale deve essere fornita attraverso una funzione che restituisce il valore (qui fatto da strigliare identity), e le funzioni devono essere elencati in un altro che è il contrario di quello che hai con una catena, che per me appare piuttosto innaturale.

+0

Sono d'accordo che questo è molto più brutto dell'altra soluzione che hai postato. –

5

OK, ho appena letto per la prima volta il codice sorgente sottolineato con il trattino basso. Ma penso che si può fare qualcosa di simile:

function double(value) { return value * 2; }; 

var obj = _([42, 43]).addToWrapper({double:double}); 

obj.chain() 
    .first() 
    .double() 
    .value(); 

la sintassi/dettagli potrebbero non essere di destra, ma il punto centrale è questo: quando si chiama _([42,43]), si sta chiamando sottolineare come una funzione. Quando lo fai, crea un'istanza di un nuovo oggetto e quindi mescola in quell'oggetto la maggior parte delle funzioni di sottolineatura. Quindi, restituisce quell'oggetto a te. È quindi possibile aggiungere le proprie funzioni a quell'oggetto e nulla di tutto questo inquina lo stesso spazio dei nomi "_".

Ecco come mi sembrava il codice underscore.js. Se sbaglio, mi piacerebbe scoprirlo e spero che qualcuno spiegherà perché.

EDIT: In realtà ho usato underscore.js pesantemente per circa un mese, e mi sono abbastanza familiarizzato con esso. Io ora so si comporta come ho detto qui. Quando chiami _ come funzione di Costruttore, recuperi il tuo "spazio dei nomi" (solo un oggetto), e puoi aggiungervi cose con addToWrapper() che appaiono nel tuo spazio dei nomi ma non nel "globale" "_" namespace. Quindi la funzionalità che l'OP voleva è già integrata. (E sono stato davvero impressionato dal trattino basso, btw, è molto ben fatto).

+0

Charlie, si, puoi farlo. Avrei dovuto menzionarlo nella mia domanda: non voglio farlo perché inquinerebbe lo spazio dei nomi '_'. (Immagina di avere una base di codice grande usando Underscore, e ogni volta che vuoi farlo devi aggiungere la funzione a Underscore.) – avernet

+2

@Alessandro Vernet - no non inquinerà lo spazio dei nomi _. Questa è la sua bellezza. Quando chiami _ ([42,43]), restituisce uno * spazio dei nomi * nuovissimo * solo per te. Quando si aggiunge "doppio" ad esso, il proprio spazio dei nomi avrà il doppio, ma lo stesso _ non lo farà. –

+0

-1 _ ([42, 43]).addToWrapper restituisce undefined –

0

Nessuno ha effettivamente risposto a questo come richiesto, quindi ecco la mia risposta alla richiesta che ho testato. Si prega di leggere la nota sotto per quanto riguarda prima.

function doubleValue(x) { return x * 2; } 
var rt = _.chain([42,43]) 
     .first(1) 
     .map(doubleValue) 
     .value(); 
console.log(rt); // prints out [84] 

// Si noti che è importante dire .first() il numero di elementi che si desidera altrimenti si può ottenere un array vuoto altrimenti. Pensa prima come VOGLIO il primo numero di oggetti, quindi.prime (2) restituisce [84], [86], ecc

+0

Questo è v.close, ma manca una chiamata extra a first() per scartare il risultato dal suo array. – arcseldon

+0

Ovviamente non hai letto il codice, è un array non un singolo valore che viene restituito, se si specifica solo .first (1) che era per un valore. Penso che tu abbia bisogno di leggere ciò che le persone scrivono prima di aggiungere commenti stupidi. –

+0

Per favore, non c'è bisogno per l'emozione :). L'OP chiedeva un valore, non un array singleton o un array di valori multipli. Sì, _.first è solo un alias per prendere in altri linguaggi come Haskell o librerie JS come Ramda. Certo, se la domanda fosse estesa per prendere N valori, questo si applicherebbe. Anche first (2) restituisce [84, 86] (dopo la mappatura) e non [84] [86] come indicato nella nota. Consiglierei Ramda - la soluzione diventa semplicemente R.pipe (R.head, R.multiply (2)) ([42, 43]); O se desideri fare ciò che stavi illustrando: R.pipe (R.take (2), R.map (R.multiply (2))) ([42, 43]) Buona fortuna – arcseldon

0

Molti modi per raggiungere facilmente questo, qui è una tale soluzione:

_.chain([42,43]) 
    .first(1) 
    .map(double) 
    .first() 
    .value(); 
    // 84 

Tuttavia, mi consiglia di utilizzare Ramda poi con auto-curry e tubi/comporre questi tipi di compiti sono banali:

R.pipe(R.head, R.multiply(2))([42, 43]); // 84 

equivalent to: 

R.compose(R.multiply(2), R.head)([42, 43]); // 84 

Se si voleva estendere la soluzione a prendere dire che i primi 2 elementi, invece di un singolo valore, come richiesto dal PO, allora:

R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43]) // [84, 86] 

Tuttavia, in Ramda R.com è preferito. Ad ogni modo, per compiti banali come questo, tende ad essere più conveniente e facile da usare.

0

Sembra lodash ha implementato esattamente quello che stai cercando:

_.thru(value, interceptor) 

dalla documentazione:

Questo metodo è come _.tap eccezione del fatto che restituisce il risultato di intercettore

https://lodash.com/docs#thru

Problemi correlati