2012-03-16 5 views
16

¿Es posible combinar el filtro y el mapa de Underscore? Actualmente tengo dos llamadas a funciones separadas, pero me pregunto si puedo hacerlas más eficientes al combinarlas en una sola llamada, de alguna manera.¿Cómo lograr la máxima eficiencia con el mapa y el filtro de subrayado?

Básicamente tengo una matriz de nombres de países - Quiero filtrarlos usando una expresión regular, luego correlacionar los resultados filtrados con una matriz de objetos DataItem. Este es mi código actual:

var filteredData = _.filter(allCountries, function(n, i){ 
    var re = RegExp("^" + searchString, "i"); 
    if (re.exec(n['country'].toLowerCase()) !== null) { 
    return true; 
    } 
}); 
var mappedData = _.map(filteredData, function(n, i){ 
    return new DataItem(i, n['name'], n['budget']); 
}); 

Cualquier otro consejo para mejorar la eficiencia también será recibido con gratitud.

Respuesta

11

Se puede utilizar en lugar each:

result = [] 
_.each(array, function(elem) { 
    if(elem.indexOf(search) == 0) 
     result.push(...whatever...) 

También tenga en cuenta que no es necesario una expresión regular sólo para averiguar si una cadena comienza con otro.

+2

La cadena es muy preferida para cada –

+0

@itcouldevenbeaboat: puede ser, no uso tanto el guión bajo ... En este caso particular 'cada' parece ser más legible. – georg

+0

Esto no es bueno porque ahora has introducido la variable 'resultado' innecesaria y mutable. Uno puede argumentar que también podría usar un bucle 'for' en este caso. 'cadena' es la forma preferida de ir. – spinningarrow

33

subrayado ofrece una capacidad de encadenamiento a través _.chain:

_.chain(allCountries) 
.filter(function(n, i) { ... }) 
.map(function(n, i) { ... }) 
.value(); // stop chaining and get the result 

En lugar de re.exec(...) !== null puede utilizar re.test(...), y tenga en cuenta que tiene que escapar caracteres especiales de expresiones regulares para searchString.

En este caso simple sin embargo, es mejor utilizar .indexOf para comprobar si la cadena comienza con una subcadena:

// substring should be apparent at position 0, discard case for both strings 
return n.country.toLowerCase().indexOf(searchString.toLowerCase()) === 0; 

Para los literales de cadena, .foo puede ser más claro que ['foo'].

+0

¡Esta es ciertamente una respuesta! – nick4fake

8

La respuesta de pimvdb es la forma en que hacemos las cosas en la programación funcional/underscore.js es una optimización prematura realizar ambos pasos al mismo tiempo. JS no se beneficia mucho al hacer estas cosas por separado.

_.chain(allCountries) 
.filter(function(n, i) { ... }) 
.map(function(n, i) { ... }) 
.value(); 

lo anterior es muy fácil de entender, pero una vez que empezar a combinar las responsabilidades cosas se ponen peludas.

_.mapFilter (array, filterFn, mapFn) ...

con el encadenamiento estamos sacrificando el rendimiento de la productividad. Ambos son importantes, pero siempre uno es más importante que el otro. No podemos retroceder y mejorar la productividad, pero podemos mejorar el rendimiento después de los hechos.

+0

¡Esto merece más votos favorables! "No podemos retroceder y mejorar la productividad, pero podemos mejorar el rendimiento después de los hechos". Concepto impresionante! –

11

Usa _.reduce como guarda n iteraciones. Saque su RegEx del bucle para que no vuelva a crear el objeto en cada iteración. Use test en lugar de exec (más rápido porque es un resultado booleano simple).

var re = RegExp("^" + searchString, "i"); 
var data = _.reduce(allCountries, function(res, n, i) { 
    if (re.test(n['country'])) { 
    res.push(new DataItem(i, n['name'], n['budget'])); 
    } 
    return res; 
}, []); 
Cuestiones relacionadas