2012-09-10 8 views
22

Necesito el índice del primer valor en la matriz, que coincide con una función de comparación personalizada.¿Hay un indexOf en javascript para buscar una matriz con la función de comparación personalizada

La muy agradable underscorej tiene una función "buscar" que devuelve el primer valor donde una función devuelve verdadero, pero necesitaría esto que devuelve el índice en su lugar. ¿Hay alguna versión de indexOf disponible en alguna parte, donde pueda pasar una función utilizada para comparar?

¡Gracias por cualquier sugerencia!

+1

Creo que su enfoque en general es incorrecto aquí. No desea que la funcionalidad modifique la funcionalidad predeterminada (sobrecarga '==='), quiere su propia funcionalidad (p. Ej., 'MyIndexOf'). El primero es más perturbador y peligroso que el segundo. – Christian

+2

lodash lo tiene http://lodash.com/docs#findIndex –

+1

Si su entorno de destino es compatible con ES2015 (o si tiene un paso de transpile, por ejemplo, con Babel), puede utilizar el Array.prototype.findIndex() nativo. – craigmichaelmartin

Respuesta

26

Esta es la forma subrayado que hacerlo - esto aumenta la función básica de subrayado con uno que acepta una función de iterador:

// save a reference to the core implementation 
var indexOfValue = _.indexOf; 

// using .mixin allows both wrapped and unwrapped calls: 
// _(array).indexOf(...) and _.indexOf(array, ...) 
_.mixin({ 

    // return the index of the first array element passing a test 
    indexOf: function(array, test) { 
     // delegate to standard indexOf if the test isn't a function 
     if (!_.isFunction(test)) return indexOfValue(array, test); 
     // otherwise, look for the index 
     for (var x = 0; x < array.length; x++) { 
      if (test(array[x])) return x; 
     } 
     // not found, return fail value 
     return -1; 
    } 

}); 

_.indexOf([1,2,3], 3); // 2 
_.indexOf([1,2,3], function(el) { return el > 2; }); // 2 
+0

gracias, ¡esto es lindo! –

+0

Underscore es excesivo. Es posible que desee envolver todo en un IIFE: acaba de introducir una dependencia en una variable global. Además, si hay un error en tu código, has contaminado todo el código con _.indexOf. – 1983

+3

@mintsauce: OP hizo referencia a Underscore, por eso ofrecí una solución basada en Underscore. W/r/t la referencia global, esto es un fragmento, no un módulo adicional; el trabajo del usuario es envolverlo o configurarlo de una manera apropiada para su aplicación. W/r/t el error - cierto, es por eso que prefiero escribir código libre de errores :). – nrabinowitz

7

Se podría hacer algo como esto:

Array.prototype.myIndexOf = function(f) 
{ 
    for(var i=0; i<this.length; ++i) 
    { 
     if(f(this[i])) 
      return i; 
    } 
    return -1; 
}; 

Con respecto al comentario de Christian: si reemplaza un método JavaScript estándar con una personalizada con una diferente la misma firma y funcionalidad diferente, lo malo es probable que suceda . Esto es especialmente cierto si está extrayendo bibliotecas de terceros que pueden depender del original, por ejemplo, Array.proto.indexOf. Entonces sí, probablemente quieras llamarlo de otra manera.

+2

Gracias por la preocupación. También creo en segundas oportunidades. ;) Por favor, resalte por qué 'Array.prototype.indexOf (function)' es el enfoque incorrecto, y le daré ese voto alternativo. – Christian

+0

Solucionado. Buena atrapada. –

+0

Gracias por este. Podría usar este sin agregarlo a Array.prototype. –

0

usando subrayar que se me ocurrió algo copiado de su aplicación de búsqueda utilizando _.any:

findIndex = function (obj, iterator, context) { 
    var idx; 
    _.any(obj, function (value, index, list) { 
     if (iterator.call(context, value, index, list)) { 
      idx = index; 
      return true; 
     } 
    }); 
    return idx; 
}; 

¿Qué opinas - ¿tienes alguna solución mejor?

+0

No sé qué significa '_.any'. ¿Supongo que estás usando algún marco? Si es así, sé amable con nosotros y cuéntanos tu secreto;). – Christian

+0

como se indica en la pregunta, underscore.js: http://underscorejs.org – billy

+1

No debe usar la función extra con 'any', ralentiza el método. Simplemente use un simple for-loop – Bergi

1

El filtro javascript método de arreglos devuelve un subconjunto de la matriz que devuelve true desde la función pasó.

var arr= [1, 2, 3, 4, 5, 6], 
first= arr.filter(function(itm){ 
    return itm>3; 
})[0]; 
alert(first); 

if you must support IE before #9 you can 'shim' Array.prototype.filter- 

Array.prototype.filter= Array.prototype.filter || function(fun, scope){ 
    var T= this, A= [], i= 0, itm, L= T.length; 
    if(typeof fun== 'function'){ 
     while(i<L){ 
      if(i in T){ 
       itm= T[i]; 
       if(fun.call(scope, itm, i, T)) A[A.length]= itm; 
      } 
      ++i; 
     } 
    } 
    return A; 
} 
+0

gracias, construir una nueva matriz de subconjuntos puede ralentizar el rendimiento, ¿qué opinas? –

+0

Creo que esta es la respuesta más simple y elegante. No requiere la definición de ningún método nuevo, guión bajo o un relleno policial ni nada. Estoy hablando estrictamente de simplicidad, no de rendimiento. –

1

¿Qué tal such find function?

(function() { 
    if (!Array.prototype._find) { 
    Array.prototype._find = function (value) { 
     var i = -1, j = this.length; 
     if (typeof(value)=="function") 
     for(; (++i < j) && !value(this[i]);); 
     else 
     for(; (++i < j) && !(this[i] === value);); 

     return i!=j ? i : -1; 
    } 
    } 
}()); 
1

Aquí viene la versión de CoffeeScript nrabinowitz de code.

# save a reference to the core implementation 
indexOfValue = _.indexOf 

# using .mixin allows both wrapped and unwrapped calls: 
# _(array).indexOf(...) and _.indexOf(array, ...) 
_.mixin ({ 
    # return the index of the first array element passing a test 
    indexOf: (array, test) -> 
     # delegate to standard indexOf if the test isn't a function 
     if (!_.isFunction(test)) 
      return indexOfValue(array, test) 
     # otherwise, look for the index 
     for item, i in array 
      return i if (test(item)) 
     # not found, return fail value 
     return -1 
}) 
3

Como otros han señalado, bastante fácil de rodar su propia, que puede mantener cortas y simples para su caso en particular:

// Find the index of the first element in array 
// meeting specified condition. 
// 
var findIndex = function(arr, cond) { 
    var i, x; 
    for (i in arr) { 
    x = arr[i]; 
    if (cond(x)) return parseInt(i); 
    } 
}; 

var moreThanTwo = function(x) { return x > 2 } 
var i = findIndex([1, 2, 3, 4], moreThanTwo) 

O si usted es un CoffeeScripter:

findIndex = (arr, cond) -> 
    for i, x of arr 
    return parseInt(i) if cond(x) 
6

Hay a proposal para Array.prototype.findIndex() en Harmony (ECMAScript 6). Actualmente está implementado en Firefox y Safari. Aquí hay un polyfill, cortesía de Mozilla Developer Network:

if (!Array.prototype.findIndex) { 
    Array.prototype.findIndex = function(predicate) { 
    if (this == null) { 
     throw new TypeError('Array.prototype.find called on null or undefined'); 
    } 
    if (typeof predicate !== 'function') { 
     throw new TypeError('predicate must be a function'); 
    } 
    var list = Object(this); 
    var length = list.length >>> 0; 
    var thisArg = arguments[1]; 
    var value; 

    for (var i = 0; i < length; i++) { 
     value = list[i]; 
     if (predicate.call(thisArg, value, i, list)) { 
     return i; 
     } 
    } 
    return -1; 
    }; 
} 
+0

findIndex en realidad llegó al estándar EcmaScript 2015 mientras tanto, ver el enlace provisto por @Husky arriba –

Cuestiones relacionadas