Come ottenere l'ennesima occorrenza in una stringa?


104

Vorrei ottenere la posizione iniziale 2nddell'occorrenza di ABCcon qualcosa del genere:

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

Come lo faresti?


La seconda ricorrenza o l'ultima? :)
Ja͢ck

Scusa per la confusione, non sto cercando l'ultimo indice. Sto cercando la posizione iniziale nthdell'occorrenza, in questo caso la seconda.
Adam

Risposte:


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
)


26
In realtà non mi piace questa risposta. Dato un input di lunghezza illimitato, crea inutilmente un array di lunghezza illimitato e poi getta via la maggior parte di esso. Sarebbe più veloce ed efficiente solo utilizzare in modo iterativo l' fromIndexargomento perString.indexOf
Alnitak

3
function getPosition(str, m, i) { return str.split(m, i).join(m).length; }
copia

9
Sarei stato bello se avessi specificato cosa significava ogni parametro.
Foreever

1
@Foreever ho semplicemente implementato la funzione definita da OP
Denys Séguret

5
Questo ti darà la lunghezza della stringa se ci sono < ioccorrenze di m. Cioè, getPosition("aaaa","a",5)4, come fa getPosition("aaaa","a",72)! Penso che tu voglia -1 in quei casi. var ret = str.split(m, i).join(m).length; return ret >= str.length ? -1 : ret;Si potrebbe anche voler cattura i <= 0conreturn ret >= str.length || i <= 0 ? -1 : ret;
Ruffin

70

È inoltre possibile utilizzare la stringa indexOf senza creare alcun array.

Il secondo parametro è l'indice per iniziare a cercare la prossima corrispondenza.

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
*/

Mi piace questa versione a causa della cache di lunghezza e non estende il prototipo String.
Christophe Roussy

8
secondo jsperf questo metodo è molto più veloce della risposta accettata
boop

L'incremento di ipuò essere reso meno 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
Preferisco questa alla risposta accettata poiché quando ho testato per una seconda istanza che non esisteva, l'altra risposta ha restituito la lunghezza della prima stringa in cui questa ha restituito -1. Un voto positivo e grazie.
Giovanni

2
È assurdo che questa non sia una caratteristica incorporata di JS.
Barba

20

Lavorando sulla risposta di kennebec, ho creato una funzione prototipo che restituirà -1 se l'ennesima occorrenza non viene trovata invece di 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;
}

2
Non usare mai camelCase poiché l'eventuale adattamento delle funzionalità in modo nativo potrebbe essere involontariamente sovrascritto da questo prototipo. In questo caso io consiglierei tutto in minuscolo e underscore (trattini per gli URL): String.prototype.nth_index_of. Anche se pensi che il tuo nome sia unico e abbastanza pazzo, il mondo dimostrerà che può farlo e lo farà di più.
Giovanni

Soprattutto durante la prototipazione. Certo, nessuno potrà mai usare quel nome di metodo specifico, anche se permettendoti di farlo crei una cattiva abitudine. Un diverso se ad esempio critica: sempre dati racchiudere quando si fa uno SQL INSERTcome mysqli_real_escape_stringnon non protegge contro hack sola offerta. Gran parte della programmazione professionale non è solo avere buone abitudini, ma anche capire perché tali abitudini sono importanti. :-)
John

1
Non estendere il prototipo di stringa.

4

Perché la ricorsione è sempre la risposta.

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;
        }
    }
}

1
@RenanCoelho La tilde ( ~) è l'operatore NOT bit per bit in JavaScript: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Sébastien

2

Ecco la mia soluzione, che si limita a scorrere la stringa fino a quando non nvengono trovate corrispondenze:

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

2

Questo metodo crea una funzione che chiama l'indice delle n-esime occorrenze memorizzate in un array

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 
}

Non penso che tu abbia definito myString nel codice sopra e non sei sicuro che myStr === myString?
Seth Eden,

1

Modo più breve e penso più facile, senza creare stringhe inutili.

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
}

0

Utilizzo indexOfe ricorsione :

Innanzitutto controlla se l'ennesima posizione passata è maggiore del numero totale di occorrenze di sottostringa. Se passato, passa ricorsivamente attraverso ogni indice fino a trovare l'ennesimo.

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);
};

0

utilizzando [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

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));

-2

Stavo giocando con il seguente codice per un'altra domanda su StackOverflow e ho pensato che potesse essere appropriato qui. La funzione printList2 consente l'uso di una regex ed elenca tutte le occorrenze in ordine. (printList era un tentativo di una soluzione precedente, ma in molti casi non è riuscito.)

<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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.