división de cadena solo en la primera instancia del carácter especificado


272

En mi código, dividí una cadena basada en _y tomé el segundo elemento de la matriz.

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

good_luckMe toma y me proporciona luck. ¡Funciona genial!

Pero, ahora tengo una clase que se parece good_luck_buddy. ¿Cómo consigo mi javascript para ignorar el segundo _y darme luck_buddy?

Encontré esto var field = element.split(new char [] {'_'}, 2);en una respuesta ac # stackoverflow pero no funciona. Lo intenté en jsFiddle ...

Respuestas:


408

Use paréntesis de captura :

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

Se definen como

Si separatorcontiene paréntesis de captura, los resultados coincidentes se devuelven en la matriz.

Entonces, en este caso, queremos dividir en _.+(es decir, el separador dividido es una subcadena que comienza con _) pero también dejar que el resultado contenga alguna parte de nuestro separador (es decir, todo lo que sigue _).

En este ejemplo, nuestro separador (coincidencia _(.+)) es _luck_buddyy el grupo capturado (dentro del separador) es lucky_buddy. Sin el paréntesis de captura, la luck_buddy(coincidencia .+) no se habría incluido en la matriz de resultados, ya que es simple splitque los separadores no se incluyan en el resultado.


21
Ni siquiera necesita (?), Solo use /_(.+)/ para capturar 1 caracteres más después del primer _
Mark

3
Muy elegante. Funciona de maravilla. Gracias.
Ofeargall

12
Para ser claros, la razón por la que esta solución funciona es porque todo después del primero _coincide con un grupo de captura y se agrega a la lista de tokens por ese motivo.
Alan Moore

28
Alguien sabe por qué obtengo un elemento de cadena vacío adicional con esto: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee

44
@katylavallee - Esto podría ayudar: stackoverflow.com/questions/12836062/… Como el separador es ": 16:9", no hay nada después del separador, creando así la cadena vacía al final.
Derek 朕 會 功夫

232

¿Para qué necesita expresiones regulares y matrices?

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

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


55
cadena! == Cadena. JavaScript distingue entre mayúsculas y minúsculas.
kennebec

3
Creo que esta es la mejor respuesta. También es posible obtener una cadena después de un segundo _escribiendo:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel

9
La respuesta genera la segunda parte de la cadena. ¿Y si quieres la primera parte también? Con var str = "good_luck_buddy", res = str.split(/_(.+)/);usted obtendrá todas las partes:console.log(res[0]); console.log(res[1]);
Dom

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Ahí lo tienes. También con soporte de aguja variable
Steffan

Este es el genio!
stuckedoverflow

36

Evito RegExp a toda costa. Aquí hay otra cosa que puedes hacer:

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

18
Al que le teme a RegExp nunca se le puede decir cuán grande es RegExp. Necesitas encontrar la puerta tú mismo. Una vez que estés allí, nunca mirarás hacia atrás. Pregúntame de nuevo en unos años y me dirás lo genial que es.
Christiaan Westerbeek

3
@yonas ¡Toma la pastilla roja!
viernes

2
@yonas Sí, ¡toma la pastilla roja! Hará tu vida más rápida, incluso para cadenas cortas: jsperf.com/split-by-first-colon
Julian F. Weinert

15
¡Decir ah! Escribí este comentario hace más de 4 años. ¡Definitivamente estoy a bordo con RegExp ahora! :)
yonas

3
@yonas, mejor no lo hagas. RegExp es increíble cuando lo necesitas . No es el caso aquí. Compruebe la prueba actualizada: jsperf.com/split-by-first-colon/2
metalim

11

Reemplace la primera instancia con un marcador de posición único y luego divídalo desde allí.

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

["good","luck_buddy"]

Esto es más útil cuando se necesitan ambos lados de la división.


3
Esto pone una restricción innecesaria en la cadena.
Yan Foto

Esta respuesta funcionó para mí cuando no todas las respuestas anteriores.
GuitarViking

1
@YanFoto, ¿quieres decir con '&'? Podría ser cualquier cosa.
sebjwallace

2
@sebjwallace Lo que elijas, significa que no puedes tener ese personaje en la cadena. Por ejemplo, "fish & chips_are_great" da [fish, chips, are_great], creo.
Joe

@ Joe Podrías usar cualquier cosa en lugar de '&', fue solo un ejemplo. Puede reemplazar la primera aparición de _ con ¬ si lo desea. Entonces "fish & chips_are_great" reemplazaría la primera aparición de _ con ¬ para dar "fish & chips¬are_great" y luego se dividirá entre ¬ para obtener ["fish & chips", "are_great"]
sebjwallace

8

Puede usar la expresión regular como:

var arr = element.split(/_(.*)/)
Puede usar el segundo parámetro que especifica el límite de la división. es decir: campo var = element.split ('_', 1) [1];

66
Eso solo especifica cuántos de los elementos divididos se devuelven, no cuántas veces se divide. 'good_luck_buddy'.split('_', 1);regresa solo['good']
Alex Vidal

Gracias hizo una suposición al respecto. Se actualizó la publicación para usar una expresión regular.
Chandu

¿Se (:?.*)suponía que era un grupo que no capturaba? Si es así, debería serlo (?:.*), pero si lo corrige, encontrará que ya no funciona. (:?.*)coincide con un opcional :seguido de cero o más de cualquier carácter. Esta solución termina funcionando por la misma razón que lo hace @ MarkF: todo después de que el primero _se agrega a la lista de tokens porque coincide en un grupo de captura. (Además, el gmodificador no tiene efecto cuando se usa en una expresión regular dividida.)
Alan Moore

Gracias, no me di cuenta. Actualizada la expresión regular y probado sobre el par de scenarion ...
Chandu

1
No funciona en ie8 y vuelvo a indexOf y substring
Igor Alekseev el

6

Esta solución me funcionó

var str = "good_luck_buddy";
var index = str.indexOf('_');
var arr = [str.slice(0, index), str.slice(index + 1)];

//arr[0] = "good"
//arr[1] = "luck_buddy"

O

var str = "good_luck_buddy";
var index = str.indexOf('_');
var [first, second] = [str.slice(0, index), str.slice(index + 1)];

//first = "good"
//second = "luck_buddy"

1
Sin embargo, esto no funciona si el divisor tiene más de 1 carácter.
haykam

5

Actualmente String.prototype.split, de hecho, le permite limitar el número de divisiones.

str.split([separator[, limit]])

...

límite opcional

Un entero no negativo que limita el número de divisiones. Si se proporciona, divide la cadena en cada aparición del separador especificado, pero se detiene cuando se han colocado entradas de límite en la matriz. Cualquier texto restante no está incluido en la matriz en absoluto.

La matriz puede contener menos entradas que el límite si se alcanza el final de la cadena antes de alcanzar el límite. Si el límite es 0, no se realiza la división.

consideración

Es posible que no funcione de la manera que espera. Esperaba que simplemente ignorara el resto de los delimitadores, pero en cambio, cuando alcanza el límite, divide la cadena restante nuevamente, omitiendo la parte después de la división de los resultados de retorno.

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C"]

Esperaba:

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B_C_D_E"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C_D_E"]

Igual que aquí. Parece que PHP se está dividiendo en "primero" y el "resto".
BananaAcid

4

String.splitDesafortunadamente , Javascript no tiene forma de limitar el número real de divisiones. Tiene un segundo argumento que especifica cuántos de los elementos divididos reales se devuelven, lo que no es útil en su caso. La solución sería dividir la cadena, quitar el primer elemento y luego volver a unir los elementos restantes ::

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

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

Veo que la función de división no ayuda, pero el uso de una expresión regular parece lograr esto. Debe especificar que se refiere a la función Split en sí, de forma nativa.
Dan Hanly

1
Interesante, esta solución reduce el problema a una solución más legible / manejable. En mi caso de convertir un nombre completo en nombre y apellido (sí, nuestros requisitos forzaron esta lógica), esta solución funcionó mejor y era más legible que las otras. Gracias
Sukima

Esto ya no es cierto :)
Kraken

3

Necesito las dos partes de la cadena, por lo tanto, regex lookbehind me ayuda con esto.

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


3

Con la ayuda de la asignación de desestructuración puede ser más legible:

let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')

2

¿La solución más rápida?

Ejecuté algunos puntos de referencia , y esta solución ganó enormemente: 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);
};

Comparación de rendimiento con otras soluciones.

El único competidor cercano era la misma línea de código, excepto el uso en substrlugar de slice.

Otras soluciones que probé involucrando splito RegExps tuvieron un gran éxito de rendimiento y fueron aproximadamente 2 órdenes de magnitud más lentas. Usar joinlos resultados de split, por supuesto, agrega una penalización de rendimiento adicional.

¿Por qué son más lentos? Cada vez que se debe crear un nuevo objeto o matriz, JS tiene que solicitar una porción de memoria del sistema operativo. Este proceso es muy lento.

Aquí hay algunas pautas generales, en caso de que esté persiguiendo puntos de referencia:

  • Las nuevas asignaciones de memoria dinámica para objetos {}o matrices [](como la que splitcrea) costarán mucho en rendimiento.
  • RegExp Las búsquedas son más complicadas y, por lo tanto, más lentas que las búsquedas de cadenas.
  • Si ya tiene una matriz, la desestructuración de las matrices es casi tan rápida como indexarlas explícitamente, y se ve increíble.

Eliminar más allá de la primera instancia

Aquí hay una solución que se dividirá e incluirá la enésima instancia. No es tan rápido, pero en la pregunta del OP, gobble(element, '_', 1)todavía es> 2 veces más rápido que una RegExpo splitsolución y puede hacer más:

/*
`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);
}

Con la definición anterior, gobble('path/to/file.txt', '/')daría el nombre del archivo y gobble('prefix_category_item', '_', 1)eliminaría el prefijo como la primera solución en esta respuesta.


  1. Las pruebas se ejecutaron en Chrome 70.0.3538.110 en macOSX 10.14.

Vamos ... Es 2019 ... ¿La gente todavía sigue haciendo microbenchmark de este tipo de cosas?
Victor Schröder

Estoy de acuerdo. Aunque el microbenchmarking es un poco interesante, debe confiar en un compilador o traductor para las optimizaciones. Quién sabe. Mb alguien que lee esto está construyendo un compilador o usando ejs / embedded y no puede usar regex. Sin embargo, esto se ve mejor para mi caso específico que una expresión regular. (
Quitaría

1

La solución de Mark F es increíble, pero no es compatible con los navegadores antiguos. La solución de Kennebec es increíble y es compatible con los navegadores antiguos, pero no admite expresiones regulares.

Entonces, si está buscando una solución que divida su cadena solo una vez, que sea compatible con navegadores antiguos y que sea compatible con regex, esta es mi solución:

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


1

Para principiantes como yo que no estamos acostumbrados a la expresión regular, esta solución alternativa funcionó:

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

El método slice () extrae una parte de una cadena y devuelve una nueva cadena y el método indexOf () devuelve la posición de la primera aparición encontrada de un valor especificado en una cadena.


Esta no es una solución alternativa, sino una forma adecuada de hacerlo;)
Victor Schröder

1

Use el replace()método de cadena con una expresión regular :

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

Esta expresión regular coincide con 0 o más caracteres antes del primero _, y el _propio. La coincidencia se reemplaza por una cadena vacía.


La document.body.innerHTMLparte aquí es completamente inútil.
Victor Schröder

@ VictorSchröder, ¿cómo espera ver la salida del fragmento sin document.body.innerHTML?
James T

2
document.bodydepende del DOM para estar presente y no funcionará en un entorno de JavaScript puro. console.loges suficiente para este propósito o simplemente deja el resultado en una variable para inspección.
Victor Schröder

@ VictorSchröder No creo que haya causado mucha confusión, pero de todos modos he editado.
James T

0

Esto funcionó para mí en Chrome + FF:

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

Si también necesita la clave, intente esto:

"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

Aquí hay un RegExp que hace el truco.

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

Primero obliga al partido a comenzar desde el principio con el '^'. Luego, coincide con cualquier número de caracteres que no son '_', en otras palabras, todos los caracteres anteriores al primer '_'.

Los '?' significa un número mínimo de caracteres que hacen que todo el patrón coincida con el '. *?' porque es seguido por '_', que luego se incluye en la coincidencia como su último carácter.

Por lo tanto, esta división () usa una parte coincidente como su 'divisor' y la elimina de los resultados. Por lo tanto, elimina todo hasta el primer '_' e incluye el resto como el segundo elemento del resultado. El primer elemento es "" que representa la parte anterior a la parte coincidente. Es "" porque el partido comienza desde el principio.

Hay otros RegExps que funcionan tan bien como /_(.*)/ dado por Chandu en una respuesta anterior.

El /^.*?_/ tiene el beneficio de que puede comprender lo que hace sin tener que saber sobre el papel especial que juegan los grupos de captura con replace ().

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.