¿Cuál es la forma más fácil de eliminar el primer carácter de una cadena?


174

Ejemplo:

[12,23,987,43

¿Cuál es la forma más rápida y eficiente de eliminar el " [", utilizando quizás un chop()pero para el primer carácter?


1
Edité mi respuesta, por lo que podría ser posible cambiar la respuesta seleccionada. Vea si puede otorgarlo a la respuesta de Jason Stirk, ya que la suya es la más rápida y es muy legible.
The Tin Man

3
Use str [1 ..- 1], es el más rápido según las respuestas a continuación.
Achyut Rastogi

1
A partir de Ruby 2.5 puede usar delete_prefixy delete_prefix!- más detalles a continuación . No he tenido tiempo de comparar, ¡pero lo haré pronto!
SRack

Actualización: he comparado los nuevos métodos ( delete_prefix\ delete_prefix!) y son bastante rápidos. No superan a los favoritos anteriores por la velocidad, pero la legibilidad significa que son excelentes opciones nuevas para tener.
SRack

Respuestas:


233

Estoy a favor de usar algo como:

asdf = "[12,23,987,43"
asdf [0] = '' 

p asdf
# >> "12,23,987,43"

Siempre estoy buscando la forma más rápida y fácil de hacer las cosas:

require 'benchmark'

N = 1_000_000

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }

end

Ejecutando en mi Mac Pro:

1.9.3
              user     system      total        real
[0]       0.840000   0.000000   0.840000 (  0.847496)
sub       1.960000   0.010000   1.970000 (  1.962767)
gsub      4.350000   0.020000   4.370000 (  4.372801)
[1..-1]   0.710000   0.000000   0.710000 (  0.713366)
slice     1.020000   0.000000   1.020000 (  1.020336)
length    1.160000   0.000000   1.160000 (  1.157882)

Actualización para incorporar una respuesta sugerida más:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Lo que resulta en:

2.1.2
              user     system      total        real
[0]       0.300000   0.000000   0.300000 (  0.295054)
sub       0.630000   0.000000   0.630000 (  0.631870)
gsub      2.090000   0.000000   2.090000 (  2.094368)
[1..-1]   0.230000   0.010000   0.240000 (  0.232846)
slice     0.320000   0.000000   0.320000 (  0.320714)
length    0.340000   0.000000   0.340000 (  0.341918)
eat!      0.460000   0.000000   0.460000 (  0.452724)
reverse   0.400000   0.000000   0.400000 (  0.399465)

Y otro que usa /^./para encontrar el primer personaje:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('[/^./]') { N.times { "[12,23,987,43"[/^./] = '' } }
  b.report('[/^\[/]') { N.times { "[12,23,987,43"[/^\[/] = '' } }
  b.report('sub+') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[/, "") } }
  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Lo que resulta en:

# >> 2.1.5
# >>               user     system      total        real
# >> [0]       0.270000   0.000000   0.270000 (  0.270165)
# >> [/^./]    0.430000   0.000000   0.430000 (  0.432417)
# >> [/^\[/]   0.460000   0.000000   0.460000 (  0.458221)
# >> sub+      0.590000   0.000000   0.590000 (  0.590284)
# >> sub       0.590000   0.000000   0.590000 (  0.596366)
# >> gsub      1.880000   0.010000   1.890000 (  1.885892)
# >> [1..-1]   0.230000   0.000000   0.230000 (  0.223045)
# >> slice     0.300000   0.000000   0.300000 (  0.299175)
# >> length    0.320000   0.000000   0.320000 (  0.325841)
# >> eat!      0.410000   0.000000   0.410000 (  0.409306)
# >> reverse   0.390000   0.000000   0.390000 (  0.393044)

Aquí hay otra actualización sobre hardware más rápido y una versión más nueva de Ruby:

2.3.1
              user     system      total        real
[0]       0.200000   0.000000   0.200000 (  0.204307)
[/^./]    0.390000   0.000000   0.390000 (  0.387527)
[/^\[/]   0.360000   0.000000   0.360000 (  0.360400)
sub+      0.490000   0.000000   0.490000 (  0.492083)
sub       0.480000   0.000000   0.480000 (  0.487862)
gsub      1.990000   0.000000   1.990000 (  1.988716)
[1..-1]   0.180000   0.000000   0.180000 (  0.181673)
slice     0.260000   0.000000   0.260000 (  0.266371)
length    0.270000   0.000000   0.270000 (  0.267651)
eat!      0.400000   0.010000   0.410000 (  0.398093)
reverse   0.340000   0.000000   0.340000 (  0.344077)

¿Por qué es tan lento gsub?

Después de hacer una búsqueda / reemplazo, gsubtiene que verificar posibles coincidencias adicionales antes de que pueda saber si ha terminado. subsolo hace uno y termina. Considere gsubque es un mínimo de dos subllamadas.

Además, es importante recordar eso gsub, y subtambién puede verse obstaculizado por expresiones regulares mal escritas que coinciden mucho más lentamente que una búsqueda de subcadenas. Si es posible, ancle la expresión regular para obtener la mayor velocidad posible. Hay respuestas aquí en Stack Overflow que lo demuestran, así que busque si desea obtener más información.


34
Es importante tener en cuenta que esto solo funcionará en Ruby 1.9. En Ruby 1.8, esto eliminará el primer byte de la cadena, no el primer carácter, que no es lo que quiere el OP.
Jörg W Mittag

+1: Siempre olvido que a una posición de cadena puede asignar no solo un solo carácter, sino que también puede insertar una subcadena. ¡Gracias!
quetzalcoatl

"[12,23,987,43".delete "["
rupweb

44
Eso lo elimina de todas las posiciones, que no es lo que el OP quería: "... para el primer personaje?".
The Tin Man

2
" what about "[12,23,987,43".shift ?"? ¿Qué pasa con "[12,23,987,43".shift NoMethodError: undefined method shift 'para "[12,23,987,43": String`?
The Tin Man

293

Similar a la respuesta de Pablo anterior, pero un limpiador de sombra:

str[1..-1]

Devolverá la matriz de 1 al último carácter.

'Hello World'[1..-1]
 => "ello World"

13
+1 Eche un vistazo a los resultados de referencia que agregué a mi respuesta. Tienes el tiempo de ejecución más rápido, además creo que es muy limpio.
The Tin Man

¿Qué pasa con el rendimiento en str[1,]comparación con lo anterior?
Bohr

1
@Bohr: str[1,]devuelve el segundo personaje ya que el rango es 1:nil. Tendría que proporcionar la longitud calculada real, o algo garantizado para ser mayor que la longitud, como, str[1,999999](use int_max, por supuesto) para obtener toda la cola. [1..-1]es más limpio y probablemente más rápido, ya que no necesita operar manualmente en longitud (vea [1..length] en el punto de referencia)
quetzalcoatl

44
Muy buena solución. Por cierto, si uno quiere eliminar el primer y último carácter:str[1..-2]
pisaruk

50

Podemos usar slice para hacer esto:

val = "abc"
 => "abc" 
val.slice!(0)
 => "a" 
val
 => "bc" 

Usando slice!podemos eliminar cualquier carácter especificando su índice.


2
Este elegante slice!(0)realmente debería ser la respuesta seleccionada, ya que usar asdf[0] = '' para eliminar el primer personaje es ridículo (al igual que usar gsub con regex y disparar una mosca con un obús).
f055

1
Si bien puede parecer poco intuitivo en la superficie, []=no requiere tanto código C subyacente, donde se slice!requiere trabajo adicional. Eso suma. El argumento podría ser "¿Cuál es más legible?" Me parece []=legible, pero vengo de un fondo C -> Perl que probablemente colorea mi pensamiento. Los desarrolladores de Java probablemente pensarían que es menos legible. Cualquiera de las dos es una forma aceptable de llevar a cabo la tarea siempre que se entienda y se pueda mantener fácilmente y no cargue la CPU de manera indebida.
The Tin Man

Okay. ¿Sabes cómo podemos medir si una función requiere mucha carga de CPU en ROR? ¿o deberíamos usar la diferencia de tiempo de ejecución en mili o nanosegundos?
balanv

18

Ruby 2.5+

A partir de Ruby 2.5 puede usar delete_prefixo delete_prefix!lograr esto de manera legible.

En este caso "[12,23,987,43".delete_prefix("[").

Más información aquí:

'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"

Nota: también puede usar esto para eliminar elementos del final de una cadena con delete_suffixydelete_suffix!

'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"

Editar:

Usando la configuración de referencia de Tin Man, también se ve bastante rápido (en las últimas dos entradas delete_py delete_p!). No bastante pip los favoritos anteriores para la velocidad, aunque es muy legible.

2.5.0
              user     system      total        real
[0]       0.174766   0.000489   0.175255 (  0.180207)
[/^./]    0.318038   0.000510   0.318548 (  0.323679)
[/^\[/]   0.372645   0.001134   0.373779 (  0.379029)
sub+      0.460295   0.001510   0.461805 (  0.467279)
sub       0.498351   0.001534   0.499885 (  0.505729)
gsub      1.669837   0.005141   1.674978 (  1.682853)
[1..-1]   0.199840   0.000976   0.200816 (  0.205889)
slice     0.279661   0.000859   0.280520 (  0.285661)
length    0.268362   0.000310   0.268672 (  0.273829)
eat!      0.341715   0.000524   0.342239 (  0.347097)
reverse   0.335301   0.000588   0.335889 (  0.340965)
delete_p  0.222297   0.000832   0.223129 (  0.228455)
delete_p!  0.225798   0.000747   0.226545 (  0.231745)


14

Si siempre quieres quitar los corchetes:

"[12,23,987,43".gsub(/^\[/, "")

Si solo desea eliminar el primer carácter y sabe que no estará en un conjunto de caracteres multibyte:

"[12,23,987,43"[1..-1]

o

"[12,23,987,43".slice(1..-1)

1
Yo usaría en "[12,23,987,43".sub(/^\[+/, "")lugar de gsub(/^\[/, ""). El primero permite que el motor regex encuentre todas las coincidencias, luego se reemplazan en una acción y da como resultado una mejora de la velocidad de 2x con Ruby 1.9.3.
The Tin Man

1
Ya que estamos tratando con cadenas, ¿debería ser esto gsub(/\A\[/, "") ?
Sagar Pandya

6

Alternativa ineficiente:

str.reverse.chop.reverse

4

Por ejemplo: a = "One Two Three"

1.9.2-p290 > a = "One Two Three"
 => "One Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "ne Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "e Two Three" 

1.9.2-p290 > a = a[1..-1]
 => " Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "wo Three" 

De esta forma, puede eliminar uno por uno el primer carácter de la cadena.


3
Esto es lo mismo que la respuesta de Jason Stirk, solo que la suya fue presentada muchos meses antes.
The Tin Man

3

Manera fácil:

str = "[12,23,987,43"

removed = str[1..str.length]

Manera impresionante:

class String
  def reverse_chop()
    self[1..self.length]
  end
end

"[12,23,987,43".reverse_chop()

(Nota: prefiera la manera fácil :))


1
Si desea preservar la semántica de "corte", podría simplemente"[12,23,987,43".reverse.chop.reverse
Chris Heald el

esa es una gran sobrecarga de rendimiento solo para quitar un personaje
Pablo Fernandez

77
¿por qué no usar [1 ..- 1] en lugar de [1..self.length]?
horseyguy

El ejemplo de parches de mono está bastante mal para esta pregunta, es simplemente irrelevante y feo IMO.
dredozubov

3

¡Gracias a @ the-tin-man por armar los puntos de referencia!

Por desgracia, no me gusta ninguna de esas soluciones. O requieren un paso adicional para obtener el resultado ( [0] = '', .strip!) o no son muy semánticos / claros sobre lo que está sucediendo ( [1..-1]: "¿Um, un rango de 1 a 1 negativo? ¿Año?"), O son lentos o largos para escribir ( .gsub, .length).

Lo que estamos intentando es un 'cambio' (en lenguaje Array), pero devolviendo los caracteres restantes, en lugar de lo que se cambió. ¡Usemos nuestro Ruby para hacer esto posible con cadenas! Podemos usar la operación de soporte rápido, pero asígnele un buen nombre y tome una discusión para especificar cuánto queremos cortar:

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Pero hay más que podemos hacer con esa operación de soporte rápida pero difícil de manejar. Mientras estamos en ello, para completar, escribamos un #shifty #firstpara String (¿por qué Array debería divertirse?), Tomando un argumento para especificar cuántos caracteres queremos eliminar desde el principio:

class String
  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

Bien, ahora tenemos una buena forma clara de extraer caracteres del frente de una cadena, con un método que sea consistente con Array#firsty Array#shift(¿cuál debería ser realmente un método de explosión?). Y también podemos obtener fácilmente la cadena modificada con #eat!. ¿Deberíamos compartir nuestro nuevo eat!poder con Array? ¡Por qué no!

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Ahora podemos:

> str = "[12,23,987,43" #=> "[12,23,987,43"
> str.eat!              #=> "12,23,987,43"
> str                   #=> "12,23,987,43"

> str.eat!(3)           #=> "23,987,43"
> str                   #=> "23,987,43"

> str.first(2)          #=> "23"
> str                   #=> "23,987,43"

> str.shift!(3)         #=> "23,"
> str                   #=> "987,43"

> arr = [1,2,3,4,5]     #=> [1, 2, 3, 4, 5] 
> arr.eat!              #=> [2, 3, 4, 5] 
> arr                   #=> [2, 3, 4, 5] 

¡Eso es mejor!


2
Recuerdo una discusión hace años en los foros de Perl acerca de tal función con el nombre de en chip()lugar de chop()(y chimp()como el análogo de chomp()).
Mark Thomas

2
str = "[12,23,987,43"

str[0] = ""

77
Es importante tener en cuenta que esto solo funcionará en Ruby 1.9. En Ruby 1.8, esto eliminará el primer byte de la cadena, no el primer carácter, que no es lo que quiere el OP.
Jörg W Mittag

0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end

0

Usando regex:

str = 'string'
n = 1  #to remove first n characters

str[/.{#{str.size-n}}\z/] #=> "tring"

0

Encuentro una buena solución str.delete(str[0])para su legibilidad, aunque no puedo dar fe de su rendimiento.


0

lista = [1,2,3,4] lista.drop (1)

# => [2,3,4]

List elimina uno o más elementos desde el inicio de la matriz, no muta la matriz y devuelve la matriz en sí misma en lugar del elemento descartado.

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.