regular palabra caracter cadena buscar array javascript regex indexof

palabra - ¿Hay una versión de String.indexOf() de JavaScript que permita expresiones regulares?



lastindexof javascript (15)

Basado en la respuesta de BaileyP. La principal diferencia es que estos métodos devuelven -1 si el patrón no puede coincidir.

Editar: Gracias a la respuesta de Jason Bunting, tengo una idea. ¿Por qué no modificar la propiedad .lastIndex de la expresión regular? Aunque esto solo funcionará para patrones con la bandera global ( /g ).

Editar: actualizado para pasar los casos de prueba.

String.prototype.regexIndexOf = function(re, startPos) { startPos = startPos || 0; if (!re.global) { var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":""); re = new RegExp(re.source, flags); } re.lastIndex = startPos; var match = re.exec(this); if (match) return match.index; else return -1; } String.prototype.regexLastIndexOf = function(re, startPos) { startPos = startPos === undefined ? this.length : startPos; if (!re.global) { var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":""); re = new RegExp(re.source, flags); } var lastSuccess = -1; for (var pos = 0; pos <= startPos; pos++) { re.lastIndex = pos; var match = re.exec(this); if (!match) break; pos = match.index; if (pos <= startPos) lastSuccess = pos; } return lastSuccess; }

En javascript, ¿hay un equivalente de String.indexOf () que toma una expresión regular en lugar de una cadena para el primer primer parámetro mientras todavía permite un segundo parámetro?

Necesito hacer algo como

str.indexOf(/[abc]/ , i);

y

str.lastIndexOf(/[abc]/ , i);

Mientras String.search () toma una expresión regular como parámetro, ¡no me permite especificar un segundo argumento!

Editar:
Esto resultó ser más difícil de lo que pensé originalmente, así que escribí una pequeña función de prueba para probar todas las soluciones proporcionadas ... supone que se han agregado regexIndexOf y regexLastIndexOf al objeto String.

function test (str) { var i = str.length +2; while (i--) { if (str.indexOf(''a'',i) != str.regexIndexOf(/a/,i)) alert ([''failed regexIndexOf '' , str,i , str.indexOf(''a'',i) , str.regexIndexOf(/a/,i)]) ; if (str.lastIndexOf(''a'',i) != str.regexLastIndexOf(/a/,i) ) alert ([''failed regexLastIndexOf '' , str,i,str.lastIndexOf(''a'',i) , str.regexLastIndexOf(/a/,i)]) ; } }

y estoy probando lo siguiente para asegurarme de que al menos para una expresión regular de un carácter, el resultado es el mismo que si usáramos indexOf

// Busca el a entre los xes
prueba (''xxx'');
prueba (''axx'');
prueba (''xax'');
prueba (''xxa'');
prueba (''axa'');
prueba (''xaa'');
prueba (''aax'');
prueba (''aaa'');


Combinando algunos de los enfoques ya mencionados (el indexOf es obviamente bastante simple), creo que estas son las funciones que harán el truco:

String.prototype.regexIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; } String.prototype.regexLastIndexOf = function(regex, startpos) { regex = (regex.global) ? regex : new RegExp(regex.source, "g" + (regex.ignoreCase ? "i" : "") + (regex.multiLine ? "m" : "")); if(typeof (startpos) == "undefined") { startpos = this.length; } else if(startpos < 0) { startpos = 0; } var stringToWorkWith = this.substring(0, startpos + 1); var lastIndexOf = -1; var nextStop = 0; while((result = regex.exec(stringToWorkWith)) != null) { lastIndexOf = result.index; regex.lastIndex = ++nextStop; } return lastIndexOf; }

Obviamente, la modificación del objeto String incorporado enviaría banderas rojas para la mayoría de las personas, pero puede ser una vez que no es tan importante; simplemente tenlo en cuenta.

ACTUALIZACIÓN: Editado regexLastIndexOf() por lo que parece imitar lastIndexOf() ahora. Por favor, avíseme si aún falla y bajo qué circunstancias.

ACTUALIZACIÓN: pasa todas las pruebas encontradas en los comentarios en esta página, y la mía. Por supuesto, eso no significa que sea a prueba de balas. Cualquier comentario apreciado.


Después de que todas las soluciones propuestas fallaran mis pruebas de una forma u otra (edición: algunas se actualizaron para pasar las pruebas después de que escribí esto) encontré la implementación de mozilla para Array.indexOf y Array.lastIndexOf

Los utilicé para implementar mi versión de String.prototype.regexIndexOf y String.prototype.regexLastIndexOf de la siguiente manera:

String.prototype.regexIndexOf = function(elt /*, from*/) { var arr = this.split(''''); var len = arr.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; }; String.prototype.regexLastIndexOf = function(elt /*, from*/) { var arr = this.split(''''); var len = arr.length; var from = Number(arguments[1]); if (isNaN(from)) { from = len - 1; } else { from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; else if (from >= len) from = len - 1; } for (; from > -1; from--) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; };

Parecen pasar las funciones de prueba que proporcioné en la pregunta.

Obviamente, solo funcionan si la expresión regular coincide con un carácter, pero eso es suficiente para mi propósito, ya que lo usaré para cosas como ([abc], / s, / W, / D)

Seguiré supervisando la pregunta en caso de que alguien proporcione una implementación mejor / más rápida / más limpia / más genérica que funcione en cualquier expresión regular.


El último índice de Jason Bunting no funciona. El mío no es óptimo, pero funciona.

//Jason Bunting''s String.prototype.regexIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; } String.prototype.regexLastIndexOf = function(regex, startpos) { var lastIndex = -1; var index = this.regexIndexOf( regex ); startpos = startpos === undefined ? this.length : startpos; while ( index >= 0 && index < startpos ) { lastIndex = index; index = this.regexIndexOf( regex, index + 1 ); } return lastIndex; }


En ciertos casos simples, puede simplificar su búsqueda hacia atrás mediante el uso de división.

function regexlast(string,re){ var tokens=string.split(re); return (tokens.length>1)?(string.length-tokens[tokens.length-1].length):null; }

Esto tiene algunos problemas serios:

  1. las coincidencias superpuestas no aparecerán
  2. el índice devuelto es para el final del partido en lugar del comienzo (está bien si tu expresión regular es una constante)

Pero en el lado bueno, es mucho menos código. Para una expresión regular de longitud constante que no se puede superponer (como //s/w/ para encontrar límites de palabras) esto es suficiente.


Las instancias del constructor de String tienen un método .search() que acepta un RegExp y devuelve el índice de la primera coincidencia.

Para iniciar la búsqueda desde una posición determinada (simulando el segundo parámetro de .indexOf() ) puede .indexOf() los primeros i caracteres:

str.slice(i).search(/re/)

Pero esto obtendrá el índice en la cadena más corta (después de cortar la primera parte) por lo que querrá agregar la longitud de la parte cortada ( i ) al índice devuelto si no fuera -1 . Esto le dará el índice en la cadena original:

function regexIndexOf(text, re, i) { var indexInSuffix = text.slice(i).search(re); return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i; }


Necesitaba una función regexIndexOf también para una matriz, así que programé una yo mismo. Sin embargo, dudo que esté optimizado, pero supongo que debería funcionar correctamente.

Array.prototype.regexIndexOf = function (regex, startpos = 0) { len = this.length; for(x = startpos; x < len; x++){ if(typeof this[x] != ''undefined'' && (''''+this[x]).match(regex)){ return x; } } return -1; } arr = []; arr.push(null); arr.push(NaN); arr[3] = 7; arr.push(''asdf''); arr.push(''qwer''); arr.push(9); arr.push(''...''); console.log(arr); arr.regexIndexOf(//d/, 4);


No es nativo, pero ciertamente puede agregar esta funcionalidad

<script type="text/javascript"> String.prototype.regexIndexOf = function( pattern, startIndex ) { startIndex = startIndex || 0; var searchResult = this.substr( startIndex ).search( pattern ); return ( -1 === searchResult ) ? -1 : searchResult + startIndex; } String.prototype.regexLastIndexOf = function( pattern, startIndex ) { startIndex = startIndex === undefined ? this.length : startIndex; var searchResult = this.substr( 0, startIndex ).reverse().regexIndexOf( pattern, 0 ); return ( -1 === searchResult ) ? -1 : this.length - ++searchResult; } String.prototype.reverse = function() { return this.split('''').reverse().join(''''); } // Indexes 0123456789 var str = ''caabbccdda''; alert( [ str.regexIndexOf( /[cd]/, 4 ) , str.regexLastIndexOf( /[cd]/, 4 ) , str.regexIndexOf( /[yz]/, 4 ) , str.regexLastIndexOf( /[yz]/, 4 ) , str.lastIndexOf( ''d'', 4 ) , str.regexLastIndexOf( /d/, 4 ) , str.lastIndexOf( ''d'' ) , str.regexLastIndexOf( /d/ ) ] ); </script>

No probé completamente estos métodos, pero parecen funcionar hasta el momento.


Para datos con coincidencias dispersas, el uso de string.search es el más rápido en todos los navegadores. Re-corta una cadena cada iteración para:

function lastIndexOfSearch(string, regex, index) { if(index === 0 || index) string = string.slice(0, Math.max(0,index)); var idx; var offset = -1; while ((idx = string.search(regex)) !== -1) { offset += idx + 1; string = string.slice(idx + 1); } return offset; }

Para información densa, hice esto. Es complejo en comparación con el método de ejecución, pero para datos densos, es 2-10 veces más rápido que cualquier otro método que probé, y aproximadamente 100 veces más rápido que la solución aceptada. Los puntos principales son:

  1. Llama a exec en la expresión regular pasada una vez para verificar que hay una coincidencia o salir antes. Lo hago usando (? = En un método similar, pero en IE comprobando con el ejecutivo es mucho más rápido.
  2. Construye y almacena en caché una expresión regular modificada en el formato ''(r). (?!. ? r) ''
  3. La nueva expresión regular se ejecuta y se devuelven los resultados de ese ejecutivo o del primer ejecutivo;

    function lastIndexOfGroupSimple(string, regex, index) { if (index === 0 || index) string = string.slice(0, Math.max(0, index + 1)); regex.lastIndex = 0; var lastRegex, index flags = ''g'' + (regex.multiline ? ''m'' : '''') + (regex.ignoreCase ? ''i'' : ''''), key = regex.source + ''$'' + flags, match = regex.exec(string); if (!match) return -1; if (lastIndexOfGroupSimple.cache === undefined) lastIndexOfGroupSimple.cache = {}; lastRegex = lastIndexOfGroupSimple.cache[key]; if (!lastRegex) lastIndexOfGroupSimple.cache[key] = lastRegex = new RegExp(''.*('' + regex.source + '')(?!.*?'' + regex.source + '')'', flags); index = match.index; lastRegex.lastIndex = match.index; return (match = lastRegex.exec(string)) ? lastRegex.lastIndex - match[1].length : index; };

jsPerf de métodos

No entiendo el propósito de las pruebas arriba. Las situaciones que requieren una expresión regular son imposibles de comparar con una llamada a indexOf, que creo que es el punto de hacer el método en primer lugar. Para que la prueba pase, tiene más sentido usar ''xxx + (?! x)'', que ajustar la forma en que itera la expresión regular.


Puedes usar substr.

str.substr(i).match(/[abc]/);


Tengo una versión corta para ti. ¡Me funciona bien!

var match = str.match(/[abc]/gi); var firstIndex = str.indexOf(match[0]); var lastIndex = str.lastIndexOf(match[match.length-1]);

Y si quieres una versión prototipo:

String.prototype.indexOfRegex = function(regex){ var match = this.match(regex); return match ? this.indexOf(match[0]) : -1; } String.prototype.lastIndexOfRegex = function(regex){ var match = this.match(regex); return match ? this.lastIndexOf(match[match.length-1]) : -1; }

EDITAR : si quieres agregar soporte para fromIndex

String.prototype.indexOfRegex = function(regex, fromIndex){ var str = fromIndex ? this.substring(fromIndex) : this; var match = str.match(regex); return match ? str.indexOf(match[0]) + fromIndex : -1; } String.prototype.lastIndexOfRegex = function(regex, fromIndex){ var str = fromIndex ? this.substring(0, fromIndex) : this; var match = str.match(regex); return match ? str.lastIndexOf(match[match.length-1]) : -1; }

Para usarlo, tan simple como esto:

var firstIndex = str.indexOfRegex(/[abc]/gi); var lastIndex = str.lastIndexOfRegex(/[abc]/gi);


Todavía no hay métodos nativos que realicen la tarea solicitada.

Aquí está el código que estoy usando. Imita el comportamiento de los métodos String.prototype.indexOf y String.prototype.lastIndexOf pero también aceptan un RegExp como argumento de búsqueda además de una cadena que representa el valor que se debe buscar.

Sí, es bastante larga como respuesta, ya que trata de seguir las normas actuales lo más cerca posible y, por supuesto, contiene una cantidad razonable de comentarios de JSDOC . Sin embargo, una vez minimizado, el código es de solo 2.27k y una vez comprimido para su transmisión, solo tiene 1023 bytes.

Los 2 métodos que esto agrega a String.prototype (usando Object.defineProperty donde esté disponible) son:

  1. searchOf
  2. searchLastOf

Pasa todas las pruebas que publicó el OP y, además, he probado las rutinas completamente en mi uso diario, y he intentado asegurarme de que funcionen en múltiples entornos, pero los comentarios y los problemas siempre son bienvenidos.

/*jslint maxlen:80, browser:true */ /* * Properties used by searchOf and searchLastOf implementation. */ /*property MAX_SAFE_INTEGER, abs, add, apply, call, configurable, defineProperty, enumerable, exec, floor, global, hasOwnProperty, ignoreCase, index, lastIndex, lastIndexOf, length, max, min, multiline, pow, prototype, remove, replace, searchLastOf, searchOf, source, toString, value, writable */ /* * Properties used in the testing of searchOf and searchLastOf implimentation. */ /*property appendChild, createTextNode, getElementById, indexOf, lastIndexOf, length, searchLastOf, searchOf, unshift */ (function () { ''use strict''; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1, getNativeFlags = new RegExp(''///([a-z]*)$'', ''i''), clipDups = new RegExp(''([//s//S])(?=[//s//S]*//1)'', ''g''), pToString = Object.prototype.toString, pHasOwn = Object.prototype.hasOwnProperty, stringTagRegExp; /** * Defines a new property directly on an object, or modifies an existing * property on an object, and returns the object. * * @private * @function * @param {Object} object * @param {string} property * @param {Object} descriptor * @returns {Object} * @see https://goo.gl/CZnEqg */ function $defineProperty(object, property, descriptor) { if (Object.defineProperty) { Object.defineProperty(object, property, descriptor); } else { object[property] = descriptor.value; } return object; } /** * Returns true if the operands are strictly equal with no type conversion. * * @private * @function * @param {*} a * @param {*} b * @returns {boolean} * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4 */ function $strictEqual(a, b) { return a === b; } /** * Returns true if the operand inputArg is undefined. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isUndefined(inputArg) { return $strictEqual(typeof inputArg, ''undefined''); } /** * Provides a string representation of the supplied object in the form * "[object type]", where type is the object type. * * @private * @function * @param {*} inputArg The object for which a class string represntation * is required. * @returns {string} A string value of the form "[object type]". * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2 */ function $toStringTag(inputArg) { var val; if (inputArg === null) { val = ''[object Null]''; } else if ($isUndefined(inputArg)) { val = ''[object Undefined]''; } else { val = pToString.call(inputArg); } return val; } /** * The string tag representation of a RegExp object. * * @private * @type {string} */ stringTagRegExp = $toStringTag(getNativeFlags); /** * Returns true if the operand inputArg is a RegExp. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isRegExp(inputArg) { return $toStringTag(inputArg) === stringTagRegExp && pHasOwn.call(inputArg, ''ignoreCase'') && typeof inputArg.ignoreCase === ''boolean'' && pHasOwn.call(inputArg, ''global'') && typeof inputArg.global === ''boolean'' && pHasOwn.call(inputArg, ''multiline'') && typeof inputArg.multiline === ''boolean'' && pHasOwn.call(inputArg, ''source'') && typeof inputArg.source === ''string''; } /** * The abstract operation throws an error if its argument is a value that * cannot be converted to an Object, otherwise returns the argument. * * @private * @function * @param {*} inputArg The object to be tested. * @throws {TypeError} If inputArg is null or undefined. * @returns {*} The inputArg if coercible. * @see https://goo.gl/5GcmVq */ function $requireObjectCoercible(inputArg) { var errStr; if (inputArg === null || $isUndefined(inputArg)) { errStr = ''Cannot convert argument to object: '' + inputArg; throw new TypeError(errStr); } return inputArg; } /** * The abstract operation converts its argument to a value of type string * * @private * @function * @param {*} inputArg * @returns {string} * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring */ function $toString(inputArg) { var type, val; if (inputArg === null) { val = ''null''; } else { type = typeof inputArg; if (type === ''string'') { val = inputArg; } else if (type === ''undefined'') { val = type; } else { if (type === ''symbol'') { throw new TypeError(''Cannot convert symbol to string''); } val = String(inputArg); } } return val; } /** * Returns a string only if the arguments is coercible otherwise throws an * error. * * @private * @function * @param {*} inputArg * @throws {TypeError} If inputArg is null or undefined. * @returns {string} */ function $onlyCoercibleToString(inputArg) { return $toString($requireObjectCoercible(inputArg)); } /** * The function evaluates the passed value and converts it to an integer. * * @private * @function * @param {*} inputArg The object to be converted to an integer. * @returns {number} If the target value is NaN, null or undefined, 0 is * returned. If the target value is false, 0 is returned * and if true, 1 is returned. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4 */ function $toInteger(inputArg) { var number = +inputArg, val = 0; if ($strictEqual(number, number)) { if (!number || number === Infinity || number === -Infinity) { val = number; } else { val = (number > 0 || -1) * Math.floor(Math.abs(number)); } } return val; } /** * Copies a regex object. Allows adding and removing native flags while * copying the regex. * * @private * @function * @param {RegExp} regex Regex to copy. * @param {Object} [options] Allows specifying native flags to add or * remove while copying the regex. * @returns {RegExp} Copy of the provided regex, possibly with modified * flags. */ function $copyRegExp(regex, options) { var flags, opts, rx; if (options !== null && typeof options === ''object'') { opts = options; } else { opts = {}; } // Get native flags in use flags = getNativeFlags.exec($toString(regex))[1]; flags = $onlyCoercibleToString(flags); if (opts.add) { flags += opts.add; flags = flags.replace(clipDups, ''''); } if (opts.remove) { // Would need to escape `options.remove` if this was public rx = new RegExp(''['' + opts.remove + '']+'', ''g''); flags = flags.replace(rx, ''''); } return new RegExp(regex.source, flags); } /** * The abstract operation ToLength converts its argument to an integer * suitable for use as the length of an array-like object. * * @private * @function * @param {*} inputArg The object to be converted to a length. * @returns {number} If len <= +0 then +0 else if len is +INFINITY then * 2^53-1 else min(len, 2^53-1). * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength */ function $toLength(inputArg) { return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER); } /** * Copies a regex object so that it is suitable for use with searchOf and * searchLastOf methods. * * @private * @function * @param {RegExp} regex Regex to copy. * @returns {RegExp} */ function $toSearchRegExp(regex) { return $copyRegExp(regex, { add: ''g'', remove: ''y'' }); } /** * Returns true if the operand inputArg is a member of one of the types * Undefined, Null, Boolean, Number, Symbol, or String. * * @private * @function * @param {*} inputArg * @returns {boolean} * @see https://goo.gl/W68ywJ * @see https://goo.gl/ev7881 */ function $isPrimitive(inputArg) { var type = typeof inputArg; return type === ''undefined'' || inputArg === null || type === ''boolean'' || type === ''string'' || type === ''number'' || type === ''symbol''; } /** * The abstract operation converts its argument to a value of type Object * but fixes some environment bugs. * * @private * @function * @param {*} inputArg The argument to be converted to an object. * @throws {TypeError} If inputArg is not coercible to an object. * @returns {Object} Value of inputArg as type Object. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.9 */ function $toObject(inputArg) { var object; if ($isPrimitive($requireObjectCoercible(inputArg))) { object = Object(inputArg); } else { object = inputArg; } return object; } /** * Converts a single argument that is an array-like object or list (eg. * arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap * (used by attributes property)) into a new Array() and returns it. * This is a partial implementation of the ES6 Array.from * * @private * @function * @param {Object} arrayLike * @returns {Array} */ function $toArray(arrayLike) { var object = $toObject(arrayLike), length = $toLength(object.length), array = [], index = 0; array.length = length; while (index < length) { array[index] = object[index]; index += 1; } return array; } if (!String.prototype.searchOf) { /** * This method returns the index within the calling String object of * the first occurrence of the specified value, starting the search at * fromIndex. Returns -1 if the value is not found. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] The location within the calling string * to start the search from. It can be any * integer. The default value is 0. If * fromIndex < 0 the entire string is * searched (same as passing 0). If * fromIndex >= str.length, the method will * return -1 unless searchValue is an empty * string in which case str.length is * returned. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, ''searchOf'', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, match, rx; if (!$isRegExp(regex)) { return String.prototype.indexOf.apply(str, args); } if ($toLength(args.length) > 1) { fromIndex = +args[1]; if (fromIndex < 0) { fromIndex = 0; } } else { fromIndex = 0; } if (fromIndex >= $toLength(str.length)) { return result; } rx = $toSearchRegExp(regex); rx.lastIndex = fromIndex; match = rx.exec(str); if (match) { result = +match.index; } return result; } }); } if (!String.prototype.searchLastOf) { /** * This method returns the index within the calling String object of * the last occurrence of the specified value, or -1 if not found. * The calling string is searched backward, starting at fromIndex. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] Optional. The location within the * calling string to start the search at, * indexed from left to right. It can be * any integer. The default value is * str.length. If it is negative, it is * treated as 0. If fromIndex > str.length, * fromIndex is treated as str.length. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, ''searchLastOf'', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, length, match, pos, rx; if (!$isRegExp(regex)) { return String.prototype.lastIndexOf.apply(str, args); } length = $toLength(str.length); if (!$strictEqual(args[1], args[1])) { fromIndex = length; } else { if ($toLength(args.length) > 1) { fromIndex = $toInteger(args[1]); } else { fromIndex = length - 1; } } if (fromIndex >= 0) { fromIndex = Math.min(fromIndex, length - 1); } else { fromIndex = length - Math.abs(fromIndex); } pos = 0; rx = $toSearchRegExp(regex); while (pos <= fromIndex) { rx.lastIndex = pos; match = rx.exec(str); if (!match) { break; } pos = +match.index; if (pos <= fromIndex) { result = pos; } pos += 1; } return result; } }); } }()); (function () { ''use strict''; /* * testing as follow to make sure that at least for one character regexp, * the result is the same as if we used indexOf */ var pre = document.getElementById(''out''); function log(result) { pre.appendChild(document.createTextNode(result + ''/n'')); } function test(str) { var i = str.length + 2, r, a, b; while (i) { a = str.indexOf(''a'', i); b = str.searchOf(/a/, i); r = [''Failed'', ''searchOf'', str, i, a, b]; if (a === b) { r[0] = ''Passed''; } log(r); a = str.lastIndexOf(''a'', i); b = str.searchLastOf(/a/, i); r = [''Failed'', ''searchLastOf'', str, i, a, b]; if (a === b) { r[0] = ''Passed''; } log(r); i -= 1; } } /* * Look for the a among the xes */ test(''xxx''); test(''axx''); test(''xax''); test(''xxa''); test(''axa''); test(''xaa''); test(''aax''); test(''aaa''); }());

<pre id="out"></pre>


Utilizar:

str.search(regex)

Vea la documentación here.


RexExp instancias lastIndex ya tienen una propiedad lastIndex (si son globales) y entonces lo que hago es copiar la expresión regular, modificarla ligeramente para adaptarla a nuestros propósitos, ejecutarla en la cadena y mirar el lastIndex . Esto inevitablemente será más rápido que hacer un bucle en la cuerda. (Tienes suficientes ejemplos de cómo poner esto en el prototipo de cuerda, ¿no?)

function reIndexOf(reIn, str, startIndex) { var re = new RegExp(reIn.source, ''g'' + (reIn.ignoreCase ? ''i'' : '''') + (reIn.multiLine ? ''m'' : '''')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; function reLastIndexOf(reIn, str, startIndex) { var src = //$$/.test(reIn.source) && !////$$/.test(reIn.source) ? reIn.source : reIn.source + ''(?![//S//s]*'' + reIn.source + '')''; var re = new RegExp(src, ''g'' + (reIn.ignoreCase ? ''i'' : '''') + (reIn.multiLine ? ''m'' : '''')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; reIndexOf(/[abc]/, "tommy can eat"); // Returns 6 reIndexOf(/[abc]/, "tommy can eat", 8); // Returns 11 reLastIndexOf(/[abc]/, "tommy can eat"); // Returns 11

También podría prototipar las funciones en el objeto RegExp:

RegExp.prototype.indexOf = function(str, startIndex) { var re = new RegExp(this.source, ''g'' + (this.ignoreCase ? ''i'' : '''') + (this.multiLine ? ''m'' : '''')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; RegExp.prototype.lastIndexOf = function(str, startIndex) { var src = //$$/.test(this.source) && !////$$/.test(this.source) ? this.source : this.source + ''(?![//S//s]*'' + this.source + '')''; var re = new RegExp(src, ''g'' + (this.ignoreCase ? ''i'' : '''') + (this.multiLine ? ''m'' : '''')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; /[abc]/.indexOf("tommy can eat"); // Returns 6 /[abc]/.indexOf("tommy can eat", 8); // Returns 11 /[abc]/.lastIndexOf("tommy can eat"); // Returns 11

Una explicación rápida de cómo estoy modificando el RegExp : para indexOf , solo tengo que asegurarme de que el indicador global esté configurado. Para lastIndexOf de estoy usando un look-ahead negativo para encontrar la última ocurrencia a menos que el RegExp ya estuviera haciendo coincidir al final de la cadena.


Well, as you are just looking to match the position of a character , regex is possibly overkill.

I presume all you want is, instead of "find first of these this character" , just find first of these characters.

This of course is the simple answer, but does what your question sets out to do, albeit without the regex part ( because you didn''t clarify why specifically it had to be a regex )

function mIndexOf( str , chars, offset ) { var first = -1; for( var i = 0; i < chars.length; i++ ) { var p = str.indexOf( chars[i] , offset ); if( p < first || first === -1 ) { first = p; } } return first; } String.prototype.mIndexOf = function( chars, offset ) { return mIndexOf( this, chars, offset ); # I''m really averse to monkey patching. }; mIndexOf( "hello world", [''a'',''o'',''w''], 0 ); >> 4 mIndexOf( "hello world", [''a''], 0 ); >> -1 mIndexOf( "hello world", [''a'',''o'',''w''], 4 ); >> 4 mIndexOf( "hello world", [''a'',''o'',''w''], 5 ); >> 6 mIndexOf( "hello world", [''a'',''o'',''w''], 7 ); >> -1 mIndexOf( "hello world", [''a'',''o'',''w'',''d''], 7 ); >> 10 mIndexOf( "hello world", [''a'',''o'',''w'',''d''], 10 ); >> 10 mIndexOf( "hello world", [''a'',''o'',''w'',''d''], 11 ); >> -1