Basically what the question says. How can I delete a character at a given index position in a string? The String class doesn't seem to have any methods to do this.
If I have a string "HELLO" I want the output to be this
["ELLO", "HLLO", "HELO", "HELO", "HELL"]
I do that using
d = Array.new(c.length){|i| c.slice(0, i)+c.slice(i+1, c.length)}
I dont know if using slice! will work here, because it will modify the original string, right?
Won't Str.slice! do it? From ruby-doc.org:
str.slice!(fixnum) => fixnum or nil [...]
Deletes the specified portion from str, and returns the portion deleted.
If you're using Ruby 1.8, you can use delete_at (mixed in from Enumerable), otherwise in 1.9 you can use slice!.
Example:
mystring = "hello"
mystring.slice!(1) # mystring is now "hllo"
# now do something with mystring
$ cat m.rb
class String
def maulin! n
slice! n
self
end
def maulin n
dup.maulin! n
end
end
$ irb
>> require 'm'
=> true
>> s = 'hello'
=> "hello"
>> s.maulin(2)
=> "helo"
>> s
=> "hello"
>> s.maulin!(1)
=> "hllo"
>> s
=> "hllo"
To avoid needing to monkey patch String you can make use of tap:
"abc".tap {|s| s.slice!(2) }
=> "ab"
If you need to leave your original string unaltered, make use of dup, eg. abc.dup.tap.
I did something like this
c.slice(0, i)+c.slice(i+1, c.length)
Where c is the string and i is the index position I want to delete. Is there a better way?
Related
I have some odd characters showing up in strings that are breaking a script. From what I can tell by put badstring to console they are "\0\0\0\0".
I'd like to test for this so I can ignore them...but how?
thought that's what blank? and empty? were for?!? :
> badstring = "\0"
=> "\u0000"
> badstring.blank?
NoMethodError: undefined method `blank?' for "\u0000":String
from (irb):97
from /Users/meltemi/.rvm/rubies/ruby-2.0.0-p195/bin/irb:16:in `<main>'
> badstring.empty?
=> false
> badstring.nil?
=> false
Edit: Trying to recreate this in irb but having trouble:
> test1 = "\0\0\0\0"
=> "\u0000\u0000\u0000\u0000"
> test2 = '\0\0\0\0'
=> "\\0\\0\\0\\0"
what I want is a "\0\0\0\0" string so I can find a way to test if mystring == "\0\0\0\0" or something of the sort.
First of all blank? is a Rails helper. Try this instead:
badstring =~ /\x00/
if this returns an integer then the given string includes "\0", if this returns nil then the given string does not include "\0".
You could just remove "\0" chars with
badstring.delete!("\0")
Full example
badstring = "\0"
badstring.delete!("\0")
badstring.empty?
#=> true
Use delete instead of delete! if you want to keep the original string around.
Seems like we need to verify the encoding and characters here. You can check the string's encoding type with "string".encoding. Then you can see which character codes are actually being used here with badstring.chars.map(&:ord). Then you can replace / remove the characters using character_code.chr(encoding).
I've currently got a string that reads something like ["green%20books"] and I'd like it to read ["green books"].
I thought Googling for this would yield a result pretty quickly but everyone just wants to turn spaces into %20s. Not the other way around.
Any help would be much appreciated!
Edit:
This is the function I'm working with and I'm confused where in here to decode the URL. I tried removing the URI.encode text but that broke the function.
def self.get_search_terms(search_url)
hash = CGI.parse(URI.parse(URI.encode(search_url)).query) #returns a hash
keywords = []
hash.each do |key, value|
if key == "q" || key == "p"
keywords << value
end
end
keywords
end
you can use the 'unencode' method of URI. (aliased as decode)
require 'uri'
URI.decode("green%20books")
# => "green books"
this will not only replaces "%20" with space, but every uri-encoded charcter, which I assume is what you want.
documentation
CGI::unescape will do what you want:
1.9.2-p320 :001 > require 'cgi'
=> true
1.9.2-p320 :002 > s = "green%20books"
=> "green%20books"
1.9.2-p320 :003 > CGI.unescape(s)
=> "green books"
Another option (as YenTheFirst mentioned) might be URI.decode. However, I read a discussion that it would be deprecated -- although that was in 2010.
Anyway, since you're asking about arrays, you would perhaps map using that method:
ary.map { |s| CGI.unescape(s) }
You can use regular expressions:
string = "green%20books"
string.gsub!('%20', ' ')
puts string
How do I remove a substring after a certain character in a string using Ruby?
new_str = str.slice(0..(str.index('blah')))
I find that "Part1?Part2".split('?')[0] is easier to read.
I'm surprised nobody suggested to use 'gsub'
irb> "truncate".gsub(/a.*/, 'a')
=> "trunca"
The bang version of gsub can be used to modify the string.
str = "Hello World"
stopchar = 'W'
str.sub /#{stopchar}.+/, stopchar
#=> "Hello W"
A special case is if you have multiple occurrences of the same character and you want to delete from the last occurrence to the end (not the first one).
Following what Jacob suggested, you just have to use rindex instead of index as rindex gets the index of the character in the string but starting from the end.
Something like this:
str = '/path/to/some_file'
puts str.slice(0, str.index('/')) # => ""
puts str.slice(0, str.rindex('/')) # => "/path/to"
We can also use partition and rpartitiondepending on whether we want to use the first or last instance of the specified character:
string = "abc-123-xyz"
last_char = "-"
string.partition(last_char)[0..1].join #=> "abc-"
string.rpartition(last_char)[0..1].join #=> "abc-123-"
I want to replace the last occurrence of a substring in Ruby. What's the easiest way?
For example, in abc123abc123, I want to replace the last abc to ABC. How do I do that?
How about
new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse
For instance:
irb(main):001:0> old_str = "abc123abc123"
=> "abc123abc123"
irb(main):002:0> pattern="abc"
=> "abc"
irb(main):003:0> replacement="ABC"
=> "ABC"
irb(main):004:0> new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse
=> "abc123ABC123"
"abc123abc123".gsub(/(.*(abc.*)*)(abc)(.*)/, '\1ABC\4')
#=> "abc123ABC123"
But probably there is a better way...
Edit:
...which Chris kindly provided in the comment below.
So, as * is a greedy operator, the following is enough:
"abc123abc123".gsub(/(.*)(abc)(.*)/, '\1ABC\3')
#=> "abc123ABC123"
Edit2:
There is also a solution which neatly illustrates parallel array assignment in Ruby:
*a, b = "abc123abc123".split('abc', -1)
a.join('abc')+'ABC'+b
#=> "abc123ABC123"
Since Ruby 2.0 we can use \K which removes any text matched before it from the returned match. Combine with a greedy operator and you get this:
'abc123abc123'.sub(/.*\Kabc/, 'ABC')
#=> "abc123ABC123"
This is about 1.4 times faster than using capturing groups as Hirurg103 suggested, but that speed comes at the cost of lowering readability by using a lesser-known pattern.
more info on \K: https://www.regular-expressions.info/keep.html
Here's another possible solution:
>> s = "abc123abc123"
=> "abc123abc123"
>> s[s.rindex('abc')...(s.rindex('abc') + 'abc'.length)] = "ABC"
=> "ABC"
>> s
=> "abc123ABC123"
When searching in huge streams of data, using reverse will definitively* lead to performance issues. I use string.rpartition*:
sub_or_pattern = "!"
replacement = "?"
string = "hello!hello!hello"
array_of_pieces = string.rpartition sub_or_pattern
( array_of_pieces[(array_of_pieces.find_index sub_or_pattern)] = replacement ) rescue nil
p array_of_pieces.join
# "hello!hello?hello"
The same code must work with a string with no occurrences of sub_or_pattern:
string = "hello_hello_hello"
# ...
# "hello_hello_hello"
*rpartition uses rb_str_subseq() internally. I didn't check if that function returns a copy of the string, but I think it preserves the chunk of memory used by that part of the string. reverse uses rb_enc_cr_str_copy_for_substr(), which suggests that copies are done all the time -- although maybe in the future a smarter String class may be implemented (having a flag reversed set to true, and having all of its functions operating backwards when that is set), as of now, it is inefficient.
Moreover, Regex patterns can't be simply reversed. The question only asks for replacing the last occurrence of a sub-string, so, that's OK, but readers in the need of something more robust won't benefit from the most voted answer (as of this writing)
You can achieve this with String#sub and greedy regexp .* like this:
'abc123abc123'.sub(/(.*)abc/, '\1ABC')
simple and efficient:
s = "abc123abc123abc"
p = "123"
s.slice!(s.rindex(p), p.size)
s == "abc123abcabc"
string = "abc123abc123"
pattern = /abc/
replacement = "ABC"
matches = string.scan(pattern).length
index = 0
string.gsub(pattern) do |match|
index += 1
index == matches ? replacement : match
end
#=> abc123ABC123
I've used this handy helper method quite a bit:
def gsub_last(str, source, target)
return str unless str.include?(source)
top, middle, bottom = str.rpartition(source)
"#{top}#{target}#{bottom}"
end
If you want to make it more Rails-y, extend it on the String class itself:
class String
def gsub_last(source, target)
return self unless self.include?(source)
top, middle, bottom = self.rpartition(source)
"#{top}#{target}#{bottom}"
end
end
Then you can just call it directly on any String instance, eg "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"
.gsub /abc(?=[^abc]*$)/, 'ABC'
Matches a "abc" and then asserts ((?=) is positive lookahead) that no other characters up to the end of the string are "abc".
How can I get the first character in a string using Ruby?
Ultimately what I'm doing is taking someone's last name and just creating an initial out of it.
So if the string was "Smith" I just want "S".
You can use Ruby's open classes to make your code much more readable. For instance, this:
class String
def initial
self[0,1]
end
end
will allow you to use the initial method on any string. So if you have the following variables:
last_name = "Smith"
first_name = "John"
Then you can get the initials very cleanly and readably:
puts first_name.initial # prints J
puts last_name.initial # prints S
The other method mentioned here doesn't work on Ruby 1.8 (not that you should be using 1.8 anymore anyway!--but when this answer was posted it was still quite common):
puts 'Smith'[0] # prints 83
Of course, if you're not doing it on a regular basis, then defining the method might be overkill, and you could just do it directly:
puts last_name[0,1]
If you use a recent version of Ruby (1.9.0 or later), the following should work:
'Smith'[0] # => 'S'
If you use either 1.9.0+ or 1.8.7, the following should work:
'Smith'.chars.first # => 'S'
If you use a version older than 1.8.7, this should work:
'Smith'.split(//).first # => 'S'
Note that 'Smith'[0,1] does not work on 1.8, it will not give you the first character, it will only give you the first byte.
"Smith"[0..0]
works in both ruby 1.8 and ruby 1.9.
For completeness sake, since Ruby 1.9 String#chr returns the first character of a string. Its still available in 2.0 and 2.1.
"Smith".chr #=> "S"
http://ruby-doc.org/core-1.9.3/String.html#method-i-chr
In MRI 1.8.7 or greater:
'foobarbaz'.each_char.first
Try this:
>> a = "Smith"
>> a[0]
=> "S"
OR
>> "Smith".chr
#=> "S"
In Rails
name = 'Smith'
name.first
>> s = 'Smith'
=> "Smith"
>> s[0]
=> "S"
Another option that hasn't been mentioned yet:
> "Smith".slice(0)
#=> "S"
Because of an annoying design choice in Ruby before 1.9 — some_string[0] returns the character code of the first character — the most portable way to write this is some_string[0,1], which tells it to get a substring at index 0 that's 1 character long.
Try this:
def word(string, num)
string = 'Smith'
string[0..(num-1)]
end
If you're using Rails You can also use truncate
> 'Smith'.truncate(1, omission: '')
#=> "S"
or for additional formatting:
> 'Smith'.truncate(4)
#=> "S..."
> 'Smith'.truncate(2, omission: '.')
#=> "S."
While this is definitely overkill for the original question, for a pure ruby solution, here is how truncate is implemented in rails
# File activesupport/lib/active_support/core_ext/string/filters.rb, line 66
def truncate(truncate_at, options = {})
return dup unless length > truncate_at
omission = options[:omission] || "..."
length_with_room_for_omission = truncate_at - omission.length
stop = if options[:separator]
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
else
length_with_room_for_omission
end
"#{self[0, stop]}#{omission}"
end
Other way around would be using the chars for a string:
def abbrev_name
first_name.chars.first.capitalize + '.' + ' ' + last_name
end
Any of these methods will work:
name = 'Smith'
puts name.[0..0] # => S
puts name.[0] # => S
puts name.[0,1] # => S
puts name.[0].chr # => S