2010-10-15 17 views
22

Usando Underscore.js, puedo escribir lo siguiente que devuelve 42:Underscore.js: la forma de las funciones habituales de la cadena

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

He función personalizada, que no forma parte de la llamada Underscore.js double():

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

me gustaría ser capaz de llamar a esta función en una cadena de subrayado, como si fuera parte de un guión bajo. Me gustaría escribir lo siguiente, que me gustaría volver 84:

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

Esto no puede trabajar desde subrayado no define double(). Podría usar tap() como en:

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

Esto es válido, pero tap se aplica la función a su argumento y devuelve el argumento, no el resultado de la función. Por lo tanto, me parece que necesitaría una especie de tap que devuelve el resultado de la función aplicada a su argumento. ¿Hay algo como esto en Underscore.js? ¿Me estoy perdiendo algo terriblemente obvio?

+4

Tenga en cuenta que 'double' es una [* futura palabra reservada *] (http://bclary.com/2004/11/07/#a-7.5.3) y las implementaciones pueden arrojar un' SyntaxError' si se usa como una Identificador – CMS

+0

Alessandro, ¿has descubierto que te di la única respuesta correcta a esta pregunta? Lo que expuse no contamina en absoluto el espacio de nombres de subrayado. La biblioteca underscore.js anticipó lo que solicitó y creó una función para eso exactamente. –

+0

Subrayar ahora tiene tanto 'tap' (ejecuta una función para modificar cada elemento) como' map' (ejecuta una función que devuelve un nuevo elemento). – CMircea

Respuesta

23

Al no encontrar un tap que devuelve los rendimientos de valor por la función se ejecuta, que definen una que puedo take y añadir a _:

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

A continuación, suponiendo que tienen:

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

me puede escribir:

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

Puede mirar take como map en objetos, en lugar de listas. ¿Quieres experimentar con esto? Ver este example on jsFiddle.

+1

'apply' podría ser un nombre mejor –

+4

@Gabe, pensé en llamarlo' apply', pero el nombre ya está definido en las funciones, y tiene una semántica diferente: 'f.apply (obj, argArray)' en lugar de 'obj.take (f, argArray)'. ¿No crees que esto podría ser confusión? (Honestamente, ya que no estoy convencido de mí mismo) – avernet

+1

Para quienes buscan, 'tap' es ahora parte del núcleo de subrayado. –

5

lo que tiene una función personalizada:

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

Usted puede utilizar mixin extender subrayado con él:

_.mixin({ double:double }); 

Ahora puede llamar a la función del objeto subrayado _:

_.double(42); // 84 

y desde el objeto envuelto de regresar de chain:

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

sí, esa es una forma de hacerlo. Algo que no me gusta de esto es que contamina '_'. Es decir. esto no es factible si utiliza el encadenamiento por todas partes en su código y, por lo tanto, tiene muchas de esas funciones. – avernet

+0

Sugerí una alternativa que requiere que agregue solo una función a '_' - vea la respuesta a mi propia pregunta. Voy a experimentar con esto y ver qué tan bien funciona en la práctica. – avernet

-1

¿Funciona map para esto?

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

edición

de la documentación, parece que map sólo funcionaría si se coloca antes de la llamada a first:

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

a la derecha, 'map' no lo corta, ya que solo funciona en la lista. Aquí necesito un tipo de 'mapa' que funcione en solo "elementos". – avernet

+0

@avernet usted podría simplemente prefijar y fijar el mapa con el primero. – arcseldon

-1

Usando compose es otra manera de hacer frente a la situación, pero creo que agregar una función como takeas I suggested earlier es una mejor solución. Sin embargo, aquí es cómo el código se vería con compose:

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

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

El valor inicial debe ser proporcionada a través de una función que devuelve ese valor (en este caso realizado por currying identity), y las funciones deben ser enumerados en otro que es el reverso de lo que tienes con una cadena, que a mí me parece antinatural.

+0

Acepto que esto es mucho más feo que la otra solución que publicaste. –

5

Bien, recién estoy leyendo el código fuente anotado por primera vez. Pero creo que se puede hacer algo como esto:

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

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

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

la sintaxis/detalles podrían no ser correcta, pero el punto central es la siguiente: cuando se llama a _([42,43]), que está llamando subrayado como una función. Cuando lo hace, crea una instancia de un nuevo objeto y luego mezcla en ese objeto la mayoría de las funciones de subrayado. Entonces, te devuelve ese objeto. A continuación, puede agregar sus propias funciones a ese objeto, y nada de esto contamina el espacio de nombres "_".

Así es como me pareció el código de underscore.js. Si me equivoco, me gustaría averiguarlo y espero que alguien me explique por qué.

EDITAR: En realidad, he usado underscore.js en gran medida durante aproximadamente un mes, y me he familiarizado bastante con él. Yo ahora se comporta como he dicho aquí. Cuando llamas _ como función de Constructor, recuperas tu propio "espacio de nombres" (solo un objeto), y puedes agregarle cosas con addToWrapper() que aparece en tu espacio de nombres pero no en el "_" global. espacio de nombres Así que la función que OP quería ya está incorporada. (Y he quedado realmente impresionado con el guión bajo, por cierto, está muy bien hecho).

+0

Charlie, sí, podrías hacer esto. Debería haber mencionado esto en mi pregunta: no quiero hacerlo ya que contaminaría el espacio de nombres '_'. (Imagínese si usted tiene una gran base de código mediante subrayado, y cada vez que desee hacer esto es necesario agregar la función de subrayar.) – avernet

+2

@Alessandro Vernet - no, no va a contaminar el espacio de nombres _. Esa es la belleza de eso. Cuando llama _ ([42,43]), devuelve un * espacio de nombres * completamente nuevo solo para usted. Cuando agregue "doble" a ella, su propio espacio de nombres tendrá el doble, pero _ no lo hará. –

+0

-1 _ ([42, 43]).addToWrapper devuelve indefinido –

0

Nadie realmente respondió esto como solicitado así que aquí está mi respuesta a la solicitud que he probado. Lea primero la nota a continuación.

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

// Tenga en cuenta que es importante contar .First() el número de elementos que desea lo contrario puede obtener una matriz vacía de otra manera. Piensa primero porque QUIERO la primera cantidad de artículos, entonces.primero (2) devuelve [84], [86] etc.

+0

Esto es v.close, pero falta una llamada adicional a first() para desenvolver el resultado de su matriz. – arcseldon

+0

Claramente, no leyó el código, no es un único valor que obtiene retornos, si solo especifica .first (1) que era para un valor. Creo que debes leer lo que escribe la gente antes de agregar comentarios tontos. –

+0

Por favor, no hay necesidad de la emoción :). El OP estaba pidiendo un valor, no una matriz singleton o una matriz de valores múltiples. Sí, _.primero es solo un alias para tomar en otros idiomas como Haskell o JS libs como Ramda. Claro, si la pregunta se extendiera para tomar N valores esto se aplicaría. También el primero (2) devuelve [84, 86] (después del mapeo) y no [84] [86] como lo indicó en la Nota. Recomendaría a Ramda: la solución simplemente se convierte en R.pipe (R.head, R.multiply (2)) ([42, 43]); O si desea hacer lo que estaba ilustrando: R.pipe (R.take (2), R.map (R.multiply (2))) ([42, 43]) Buena suerte – arcseldon

0

muchas maneras de lograr esto fácilmente, aquí es una solución de este tipo:

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

Sin embargo, yo recomendaría usar Ramda continuación, con auto-curry y tubería/componer este tipo de tareas son triviales:

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

equivalent to: 

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

Si quería extender la solución para sacar decir los 2 primeros artículos, en lugar de un único valor, según lo solicitado por el PO, entonces:

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

Sin embargo, en Ramda se recomienda Rposepose. De cualquier manera, para tareas triviales como esta, tiende a ser más conveniente y fácil de usar.

0

Parece que lodash ha implementado exactamente lo que busca:

_.thru(value, interceptor) 

de los documentos:

Este método es como _.tap excepto que devuelve el resultado de interceptor

https://lodash.com/docs#thru

Cuestiones relacionadas