¿Cómo obtener la enésima aparición en una cadena?

104

Me gustaría obtener la posición inicial de la 2ndaparición de ABCalgo como esto:

var string = "XYZ 123 ABC 456 ABC 789 ABC";
getPosition(string, 'ABC', 2) // --> 16

¿Como lo harias?

Adán
fuente
¿La segunda aparición o la última? :)
Ja͢ck
Perdón por la confusión, no estoy buscando el último índice. Estoy buscando la posición inicial de nthocurrencia, en este caso la segunda.
Adam

Respuestas:

158

const string = "XYZ 123 ABC 456 ABC 789 ABC";

function getPosition(string, subString, index) {
  return string.split(subString, index).join(subString).length;
}

console.log(
  getPosition(string, 'ABC', 2) // --> 16
)

Denys Séguret
fuente
26
De hecho, no me gusta esta respuesta. Dada una entrada de longitud ilimitada, crea innecesariamente una matriz de longitud ilimitada y luego desecha la mayor parte. Sería más rápido y más eficiente usar iterativamente el fromIndexargumento paraString.indexOf
Alnitak
3
function getPosition(str, m, i) { return str.split(m, i).join(m).length; }
copiar
9
Hubiera sido bueno si especificaras lo que significa cada parámetro.
partir del
1
@Foreever Simplemente implementé la función definida por OP
Denys Séguret
5
Esto le dará la longitud de la cadena si hay < iocurrencias de m. Es decir, getPosition("aaaa","a",5)da 4, como hace getPosition("aaaa","a",72)! Creo que quieres -1 en esos casos. var ret = str.split(m, i).join(m).length; return ret >= str.length ? -1 : ret;También es posible que desee ponerse i <= 0en return ret >= str.length || i <= 0 ? -1 : ret;
contacto
70

También puede utilizar la cadena indexOf sin crear matrices.

El segundo parámetro es el índice para comenzar a buscar la siguiente coincidencia.

function nthIndex(str, pat, n){
    var L= str.length, i= -1;
    while(n-- && i++<L){
        i= str.indexOf(pat, i);
        if (i < 0) break;
    }
    return i;
}

var s= "XYZ 123 ABC 456 ABC 789 ABC";

nthIndex(s,'ABC',3)

/*  returned value: (Number)
24
*/
Kennebec
fuente
Me gusta esta versión debido al almacenamiento en caché de la longitud y no extendiendo el prototipo de String.
Christophe Roussy
8
de acuerdo con jsperf, este método es mucho más rápido que la respuesta aceptada
boop
El incremento de ise puede hacer menos confuso:var i; for (i = 0; n > 0 && i !== -1; n -= 1) { i = str.indexOf(pat, /* fromIndex */ i ? (i + 1) : i); } return i;
hlfcoding
1
Prefiero esta a la respuesta aceptada, ya que cuando probé una segunda instancia que no existía, la otra respuesta devolvió la longitud de la primera cadena donde esta devolvió -1. Un voto a favor y gracias.
Juan
2
Es absurdo que esta no sea una característica incorporada de JS.
Sinister Beard
20

Trabajando con la respuesta de kennebec, creé una función prototipo que devolverá -1 si no se encuentra la enésima aparición en lugar de 0.

String.prototype.nthIndexOf = function(pattern, n) {
    var i = -1;

    while (n-- && i++ < this.length) {
        i = this.indexOf(pattern, i);
        if (i < 0) break;
    }

    return i;
}
ilovett
fuente
2
Nunca jamás utilizar camelCase como la posible adaptación de las características de forma nativa podría convertirse involuntariamente sobrescritos por este prototipo. En este caso te recomiendo todo en minúsculas y guiones (guiones para las direcciones URL): String.prototype.nth_index_of. Incluso si crees que tu nombre es único y lo suficientemente loco, el mundo demostrará que puede hacerlo y lo hará más loco.
Juan
Especialmente eso al hacer prototipos. Claro, nadie puede usar ese nombre de método específico, aunque al permitirte hacerlo, creas un mal hábito. Un ejemplo diferente, aunque crítico: siempre adjunte datos cuando haga un SQL, INSERTya mysqli_real_escape_stringque no protege contra ataques de comillas simples. Gran parte de la codificación profesional no se trata solo de tener buenos hábitos, sino también de comprender por qué dichos hábitos son importantes. :-)
Juan
1
No extienda el prototipo de cuerda.
4

Porque la recursividad es siempre la respuesta.

function getPosition(input, search, nth, curr, cnt) {
    curr = curr || 0;
    cnt = cnt || 0;
    var index = input.indexOf(search);
    if (curr === nth) {
        if (~index) {
            return cnt;
        }
        else {
            return -1;
        }
    }
    else {
        if (~index) {
            return getPosition(input.slice(index + search.length),
              search,
              nth,
              ++curr,
              cnt + index + search.length);
        }
        else {
            return -1;
        }
    }
}
Florian Margaine
fuente
1
@RenanCoelho La tilde ( ~) es el operador NOT bit a bit en JavaScript: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Sébastien
2

Aquí está mi solución, que solo itera sobre la cadena hasta que se nencuentran coincidencias:

String.prototype.nthIndexOf = function(searchElement, n, fromElement) {
    n = n || 0;
    fromElement = fromElement || 0;
    while (n > 0) {
        fromElement = this.indexOf(searchElement, fromElement);
        if (fromElement < 0) {
            return -1;
        }
        --n;
        ++fromElement;
    }
    return fromElement - 1;
};

var string = "XYZ 123 ABC 456 ABC 789 ABC";
console.log(string.nthIndexOf('ABC', 2));

>> 16
Alnitak
fuente
2

Este método crea una función que llama al índice de enésimas ocurrencias almacenadas en una matriz

function nthIndexOf(search, n) { 
    var myArray = []; 
    for(var i = 0; i < myString.length; i++) { //loop thru string to check for occurrences
        if(myStr.slice(i, i + search.length) === search) { //if match found...
            myArray.push(i); //store index of each occurrence           
        }
    } 
    return myArray[n - 1]; //first occurrence stored in index 0 
}
Sharon Choe
fuente
No creo que haya definido myString en el código anterior y no estoy seguro de si myStr === myString?
Seth Eden
1

Forma más corta y creo que más fácil, sin crear cadenas innecesarias.

const findNthOccurence = (string, nth, char) => {
  let index = 0
  for (let i = 0; i < nth; i += 1) {
    if (index !== -1) index = string.indexOf(char, index + 1)
  }
  return index
}
Piotr
fuente
0

Uso indexOfy recursividad :

Primero verifique si la enésima posición pasada es mayor que el número total de ocurrencias de subcadenas. Si se aprueba, recorra de forma recursiva cada índice hasta encontrar el enésimo.

var getNthPosition = function(str, sub, n) {
    if (n > str.split(sub).length - 1) return -1;
    var recursePosition = function(n) {
        if (n === 0) return str.indexOf(sub);
        return str.indexOf(sub, recursePosition(n - 1) + 1);
    };
    return recursePosition(n);
};
Eric Amshukov
fuente
0

Utilizando [String.indexOf][1]

var stringToMatch = "XYZ 123 ABC 456 ABC 789 ABC";

function yetAnotherGetNthOccurance(string, seek, occurance) {
    var index = 0, i = 1;

    while (index !== -1) {
        index = string.indexOf(seek, index + 1);
        if (occurance === i) {
           break;
        }
        i++;
    }
    if (index !== -1) {
        console.log('Occurance found in ' + index + ' position');
    }
    else if (index === -1 && i !== occurance) {
        console.log('Occurance not found in ' + occurance + ' position');
    }
    else {
        console.log('Occurance not found');
    }
}

yetAnotherGetNthOccurance(stringToMatch, 'ABC', 2);

// Output: Occurance found in 16 position

yetAnotherGetNthOccurance(stringToMatch, 'ABC', 20);

// Output: Occurance not found in 20 position

yetAnotherGetNthOccurance(stringToMatch, 'ZAB', 1)

// Output: Occurance not found
sk8terboi87 ツ
fuente
0
function getStringReminder(str, substr, occ) {
   let index = str.indexOf(substr);
   let preindex = '';
   let i = 1;
   while (index !== -1) {
      preIndex = index;
      if (occ == i) {
        break;
      }
      index = str.indexOf(substr, index + 1)
      i++;
   }
   return preIndex;
}
console.log(getStringReminder('bcdefgbcdbcd', 'bcd', 3));
Arul Benito
fuente
-2

Estaba jugando con el siguiente código para otra pregunta sobre StackOverflow y pensé que podría ser apropiado aquí. La función printList2 permite el uso de una expresión regular y enumera todas las ocurrencias en orden. (printList fue un intento de una solución anterior, pero falló en varios casos).

<html>
<head>
<title>Checking regex</title>
<script>
var string1 = "123xxx5yyy1234ABCxxxabc";
var search1 = /\d+/;
var search2 = /\d/;
var search3 = /abc/;
function printList(search) {
   document.writeln("<p>Searching using regex: " + search + " (printList)</p>");
   var list = string1.match(search);
   if (list == null) {
      document.writeln("<p>No matches</p>");
      return;
   }
   // document.writeln("<p>" + list.toString() + "</p>");
   // document.writeln("<p>" + typeof(list1) + "</p>");
   // document.writeln("<p>" + Array.isArray(list1) + "</p>");
   // document.writeln("<p>" + list1 + "</p>");
   var count = list.length;
   document.writeln("<ul>");
   for (i = 0; i < count; i++) {
      document.writeln("<li>" +  "  " + list[i] + "   length=" + list[i].length + 
          " first position=" + string1.indexOf(list[i]) + "</li>");
   }
   document.writeln("</ul>");
}
function printList2(search) {
   document.writeln("<p>Searching using regex: " + search + " (printList2)</p>");
   var index = 0;
   var partial = string1;
   document.writeln("<ol>");
   for (j = 0; j < 100; j++) {
       var found = partial.match(search);
       if (found == null) {
          // document.writeln("<p>not found</p>");
          break;
       }
       var size = found[0].length;
       var loc = partial.search(search);
       var actloc = loc + index;
       document.writeln("<li>" + found[0] + "  length=" + size + "  first position=" + actloc);
       // document.writeln("  " + partial + "  " + loc);
       partial = partial.substring(loc + size);
       index = index + loc + size;
       document.writeln("</li>");
   }
   document.writeln("</ol>");

}
</script>
</head>
<body>
<p>Original string is <script>document.writeln(string1);</script></p>
<script>
   printList(/\d+/g);
   printList2(/\d+/);
   printList(/\d/g);
   printList2(/\d/);
   printList(/abc/g);
   printList2(/abc/);
   printList(/ABC/gi);
   printList2(/ABC/i);
</script>
</body>
</html>

Bradley Ross
fuente