web-dev-qa-db-fra.com

chaîne fendue uniquement à la première occurrence du caractère spécifié

Dans mon code, je divise une chaîne basée sur _ et récupère le deuxième élément du tableau.

var element = $(this).attr('class');
var field = element.split('_')[1];

Prend good_luck et me fournit luck. Fonctionne très bien!

Mais maintenant, j'ai une classe qui ressemble à good_luck_buddy. Comment faire en sorte que mon javascript ignore le second _ et me donne luck_buddy?

J'ai trouvé cette var field = element.split(new char [] {'_'}, 2); dans une réponse c # stackoverflow mais cela ne fonctionne pas. Je l'ai essayé à JsFiddle ...

195
Ofeargall

Utilisez capture des parenthèses :

"good_luck_buddy".split(/_(.+)/)[1]
"luck_buddy"

Ils sont définis comme

Si separator contient des parenthèses de capture, les résultats correspondants sont renvoyés dans le tableau.

Donc, dans ce cas, nous souhaitons scinder à _.+ (c'est-à-dire que le séparateur est une sous-chaîne commençant par _) mais aussi laissez le résultat contient une partie de notre séparateur (c'est-à-dire tout après _).

Dans cet exemple, notre séparateur (correspondant à _(.+)) est _luck_buddy et le groupe capturé (à l'intérieur du séparateur) est lucky_buddy. Sans les parenthèses de capture, le luck_buddy (correspondant .+) n'aurait pas été inclus dans le tableau de résultats, comme c'est le cas avec la simple split que les séparateurs ne sont pas inclus dans le résultat.

347
Mark F

Pourquoi avez-vous besoin d'expressions régulières et de tableaux?

myString = myString.substring(myString.indexOf('_')+1)

var myString= "hello_there_how_are_you"
myString = myString.substring(myString.indexOf('_')+1)
console.log(myString)

191
kennebec

J'évite RegExp à tout prix. Voici une autre chose que vous pouvez faire:

"good_luck_buddy".split('_').slice(1).join('_')
23
yonas

Remplacez la première instance par un espace réservé unique, puis en vous séparant.

"good_luck_buddy".replace(/\_/,'&').split('&')

["good","luck_buddy"]

Ceci est plus utile lorsque les deux côtés de la scission sont nécessaires.

10
sebjwallace

Vous pouvez utiliser l'expression régulière comme:

var arr = element.split(/_(.*)/)
Vous pouvez utiliser le deuxième paramètre qui spécifie la limite du fractionnement . var field = element.split ('_', 1) [1];
5
Chandu

Le code String.split de Javascript n'a malheureusement aucun moyen de limiter le nombre réel de scissions. Il a un deuxième argument qui spécifie le nombre d’éléments fractionnés retournés, ce qui n’est pas utile dans votre cas. La solution serait de scinder la chaîne, de décaler le premier élément, puis de rejoindre les éléments restants:

var element = $(this).attr('class');
var parts = element.split('_');

parts.shift(); // removes the first item from the array
var field = parts.join('_');
3
Alex Vidal

La solution la plus rapide?

J'ai couru quelques points de repère , et cette solution a énormément gagné:1

str.slice(str.indexOf(delim) + delim.length)

// as function
function gobbleStart(str, delim) {
    return str.slice(str.indexOf(delim) + delim.length);
}

// as polyfill
String.prototype.gobbleStart = function(delim) {
    return this.slice(this.indexOf(delim) + delim.length);
};

Comparaison des performances avec d'autres solutions

Le seul concurrent proche était la même ligne de code, à l'exception de l'utilisation de substr au lieu de split.

D'autres solutions que j'ai essayées impliquant split ou RegExps ont eu un impact important sur les performances et ont été environ 2 ordres de grandeur moins rapides. L'utilisation de join sur les résultats de split ajoute bien entendu une pénalité de performance supplémentaire.

Pourquoi sont-ils plus lents? Chaque fois qu'un nouvel objet ou un nouveau tableau doit être créé, JS doit demander un bloc de mémoire au système d'exploitation. Ce processus est très lent. 

Voici quelques directives générales, au cas où vous poursuivriez des points de repère:

  • Les nouvelles allocations de mémoire dynamiques pour les objets {} ou les tableaux [] (comme celui créé par split) vont coûter cher en performances.
  • Les recherches RegExp sont plus compliquées et donc plus lentes que les recherches par chaîne.
  • Si vous avez déjà un tableau, la déstructuration des tableaux est à peu près aussi rapide que leur indexation explicite.

Retrait au-delà de la première instance

Voici une solution qui va trancher jusqu’à la nième instance incluse. Ce n'est pas aussi rapide, mais à la question du PO, gobble(element, '_', 1) est toujours> 2x plus rapide qu'une solution RegExp ou split et peut en faire plus:

/*
`gobble`, given a positive, non-zero `limit`, deletes
characters from the beginning of `haystack` until `needle` has
been encountered and deleted `limit` times or no more instances
of `needle` exist; then it returns what remains. If `limit` is
zero or negative, delete from the beginning only until `-(limit)`
occurrences or less of `needle` remain.
*/
function gobble(haystack, needle, limit = 0) {
  let remain = limit;
  if (limit <= 0) { // set remain to count of delim - num to leave
    let i = 0;
    while (i < haystack.length) {
      const found = haystack.indexOf(needle, i);
      if (found === -1) {
        break;
      }
      remain++;
      i = found + needle.length;
    }
  }

  let i = 0;
  while (remain > 0) {
    const found = haystack.indexOf(needle, i);
    if (found === -1) {
      break;
    }
    remain--;
    i = found + needle.length;
  }
  return haystack.slice(i);
}

Avec la définition ci-dessus, gobble('path/to/file.txt', '/') donnerait le nom du fichier et gobble('prefix_category_item', '_', 1) enlèverait le préfixe comme la première solution de cette réponse.


  1. Les tests ont été effectués sur Chrome 70.0.3538.110 sur macOSX 10.14.
0
Chaim Leib Halbert

J'ai besoin des deux parties de la chaîne, alors, aidez-moi avec cette regex.

const full_name = 'Maria do Bairro';
const [first_name, last_name] = full_name.split(/(?<=^[^ ]+) /);
console.log(first_name);
console.log(last_name);

0

Voici un RegExp qui fait le tour.

'good_luck_buddy' . split(/^.*?_/)[1] 

D'abord, il oblige le match à partir du Commence avec le '^'. Ensuite, il correspond à n'importe quel nombre De caractères qui ne sont pas '_', autrement dit Tous les caractères précédant le premier '_'.

Le '?' signifie un nombre minimal de caractères qui font que la correspondance du motif entier est assortie par le caractère '. *?' parce qu'il est suivipar '_', qui est ensuite inclus dans la correspondance en tant que dernier caractère. 

Par conséquent, cette méthode split () utilise une telle partie correspondante Comme séparateur et la supprime des résultats Ainsi, il supprime tout Jusqu'au premier inclus '_' et Vous donne le reste en tant que deuxième élément de Le résultat. Le premier élément est "" représentant La partie précédant la partie correspondante. C'est "" Car le match commence depuis le début.

Il y a d'autres RegExps qui fonctionnent comme Bien comme /_(.*)/ donnés par Chandu dans une réponse précédente. 

Le /^.*?_/ présente l'avantage que vous Pouvez comprendre ce qu'il fait sans Avoir à connaître le rôle spécial Les groupes de capture jouent avec replace ().

0
Panu Logic

Utilisez la chaîne replace() method avec un regex :

var result = "good_luck_buddy".replace(/.*?_/, "");
console.log(result);

Cette expression rationnelle correspond à 0 ou plusieurs caractères avant le premier _ et au _ lui-même. La correspondance est ensuite remplacée par une chaîne vide.

0
James T

Cela a fonctionné pour moi sur Chrome + FF:

"foo=bar=beer".split(/^[^=]+=/)[1] // "bar=beer"
"foo==".split(/^[^=]+=/)[1] // "="
"foo=".split(/^[^=]+=/)[1] // ""
"foo".split(/^[^=]+=/)[1] // undefined

Si vous avez également besoin de la clé, essayez ceci:

"foo=bar=beer".split(/^([^=]+)=/) // Array [ "", "foo", "bar=beer" ]
"foo==".split(/^([^=]+)=/) // [ "", "foo", "=" ]
"foo=".split(/^([^=]+)=/) // [ "", "foo", "" ]
"foo".split(/^([^=]+)=/) // [ "foo" ]

//[0] = ignored (holds the string when there's no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)
0
oriadam

Pour les débutants comme moi qui ne sont pas habitués à l’expression régulière, cette solution de contournement fonctionne: 

   var field = "Good_Luck_Buddy";
   var newString = field.slice( field.indexOf("_")+1 );

la méthode slice () extrait une partie d'une chaîne et renvoie une nouvelle chaîne, tandis que la méthode indexOf () renvoie la position de la première occurrence trouvée d'une valeur spécifiée dans une chaîne.

0
MZulkarnain Jaranee

La solution de Mark F est géniale mais elle n'est pas supportée par les anciens navigateurs. La solution de Kennebec est géniale et supportée par les anciens navigateurs, mais ne supporte pas regex.

Donc, si vous recherchez une solution qui divise votre chaîne une seule fois, qui est prise en charge par les anciens navigateurs et prend en charge regex, voici ma solution:

String.prototype.splitOnce = function(regex)
{
    var match = this.match(regex);
    if(match)
    {
        var match_i = this.indexOf(match[0]);
        
        return [this.substring(0, match_i),
        this.substring(match_i + match[0].length)];
    }
    else
    { return [this, ""]; }
}

var str = "something/////another thing///again";

alert(str.splitOnce(/\/+/)[1]);

0
pmrotule