How can I replace characters from string without changing its object_id?
For example:
string = "this is a test"
The first 7 characters need to be replaced with capitalized characters like: "THIS IS a Test" and the object_id needs to be the same. In which way can I sub or replace the characters to make it happen?
You can do it like this:
string = "this is a test"
string[0, 7] = string[0, 7].upcase
With procedural languages, one might write the equivalent of:
string = "this is in jest"
string.object_id
#=> 70309969974760
(1..7).each { |i| string[i] = string[i].upcase }
#=> 1..7
string
#=> "tHIS IS in jest"
string.object_id
#=> 70309969974760
This is not very Ruby-like, but it does offer the advantage over #sawa's solution that it does not create a temporary 7-character string. (Well, it does create a one-character string.) This is unimportant for strings of reasonable length (and for those I'd certainly concur with sawa), but it could be significant for really, really, really long strings.
Another way to do this is as follows:
string.each_char.with_index { |c,i|
string[i] = string[i].upcase if (1..7).cover?(i) }
#=> "tHIS IS in jest"
string.object_id
#=> 70309969974760
This second way might be more efficient if string is not much larger than string[start_index..end_index].
Edit:
In a comment the OP indicates that the string is to be stripped, squeeze and reversed as well as certain characters converted to upper case. That could be done on the string in place, without creating a copy, as follows:
def strip_upcase_squeeze_reverse_whew(string, upcase_range, squeeze_str=nil)
string.strip!
upcase_range.each { |i| string[i] = string[i].upcase }
squeeze_str.nil? ? string.squeeze! : string.squeeze!(squeeze_str)
string.reverse!
end
I have assumed the four operations would be performed in a particular order, but if the order should be different, that's an easy fix.
string = " this may bee inn jest, butt it's alsoo a test "
string.object_id
#=> 70309970103280
strip_upcase_squeeze_reverse_whew(string, (1..7))
#=> "tset a osla s'ti tub ,tsej ni eb YAM SIHt"
string.object_id
#=> 70309970103280
The steps:
string = "this may bee inn jest, butt it's alsoo a test"
#=> "this may bee inn jest, butt it's alsoo a test"
upcase_range = (1..7)
#=> 1..7
string.strip!
#=> nil
string
#=> "this may bee inn jest, butt it's alsoo a test"
upcase_range.each { |i| string[i] = string[i].upcase }
#=> 1..7
string
#=> "tHIS MAY bee inn jest, butt it's alsoo a test"
squeeze_str.nil? ? string.squeeze! : string.squeeze!(squeeze_str)
#=> "tHIS MAY be in jest, but it's also a test"
string
#=> "tHIS MAY be in jest, but it's also a test"
string.reverse!
#=> "tset a osla s'ti tub ,tsej ni eb YAM SIHt"
Notice that in this example, strip! does not remove any characters, and therefore returns nil. Similarly, squeeze! would return nil if there is nothing to squeeze. It is for that reason that strip! and squeeze cannot be chained.
A second example:
string = " thiiiis may beeee in jeeest"
strip_upcase_squeeze_reverse_whew(string, (12..14), "aeiouAEIOU")
Adding onto a string without changing its object id:
foo = "foo"
# => "foo"
foo.object_id
# => 70196045363960
foo << "bar"
# => "foobar"
foo.object_id
# => 70196045363960
Replace an entire string without changing its object id
foo
# => "foo"
foo.object_id
# => 70196045363960
foo.gsub!(/./, '') << 'bar'
# => 'bar'
foo.object_id
# => 70196045363960
Replace part of a string without changing its object id
foo
# => "foo"
foo.object_id
# => 70196045363960
foo.gsub!(/o/, 'z')
# => 'fzz'
foo.object_id
# => 70196045363960
Related
How to replace a file in Ruby, but do not touch commented-out lines? To be more specific I want to change variable in configuration file. An example would be:
irb(main):014:0> string = "#replaceme\n\t\s\t\s# replaceme\nreplaceme\n"
=> "#replaceme\n\t \t # replaceme\nreplaceme\n"
irb(main):015:0> puts string.gsub(%r{replaceme}, 'replaced')
#replaced
# replaced
replaced
=> nil
irb(main):016:0>
Desired output:
#replaceme
# replaceme
replaced
I don't fully understand the question. To do a find and replace in each line, disregarding text following a pound sign, one could do the following.
def replace_em(str, source, replacement)
str.split(/(\#.*?$)/).
map { |s| s[0] == '#' ? s : s.gsub(source, replacement) }.
join
end
str = "It was known that # that dog has fleas, \nbut who'd know that that dog # wouldn't?"
replace_em(str, "that", "the")
#=> "It was known the # that dog has fleas, \nbut who'd know the the dog # wouldn't?"
str = "#replaceme\n\t\s\t\s# replaceme\nreplaceme\n"
replace_em(str, "replaceme", "replaced")
#=> "#replaceme\n\t \t # replaceme\nreplaced\n"
For the string
str = "It was known that # that dog has fleas, \nbut who'd know that that dog # wouldn't?"
source = "that"
replacement = "the"
the steps are as follows.
a = str.split(/(\#.*?$)/)
#=> ["It was known that ", "# that dog has fleas, ",
# "\nbut who'd know that that dog ", "# wouldn't?"]
Note that the body of the regular expression must be put in a capture group in order that the text used to split the string be included as elements in the resulting array. See String#split.
b = a.map { |s| s[0] == '#' ? s : s.gsub(source, replacement) }
#=> ["It was known the ", "# that dog has fleas, ",
# "\nbut who'd know the the dog ", "# wouldn't?"]
b.join
#=> "It was known the # that dog has fleas, \nbut who'd know the the dog # wouldn't?"
How about this?
puts string.gsub(%r{^replaceme}, 'replaced')
I need to clean up a string from the phrase "not" and hashtags(#). (I also have to get rid of spaces and capslock and return them in arrays, but I got the latter three taken care of.)
Expectation:
"not12345" #=> ["12345"]
" notabc " #=> ["abc"]
"notone, nottwo" #=> ["one", "two"]
"notCAPSLOCK" #=> ["capslock"]
"##doublehash" #=> ["doublehash"]
"h#a#s#h" #=> ["hash"]
"#notswaggerest" #=> ["swaggerest"]
This is the code I have
def some_method(string)
string.split(", ").map{|n| n.sub(/(not)/,"").downcase.strip}
end
All of the above test does what I need to do except for the hash ones. I don't know how to get rid of the hashes; I have tried modifying the regex part: n.sub(/(#not)/), n.sub(/#(not)/), n.sub(/[#]*(not)/) to no avail. How can I make Regex to remove #?
arr = ["not12345", " notabc", "notone, nottwo", "notCAPSLOCK",
"##doublehash:", "h#a#s#h", "#notswaggerest"].
arr.flat_map { |str| str.downcase.split(',').map { |s| s.gsub(/#|not|\s+/,"") } }
#=> ["12345", "abc", "one", "two", "capslock", "doublehash:", "hash", "swaggerest"]
When the block variable str is set to "notone, nottwo",
s = str.downcase
#=> "notone, nottwo"
a = s.split(',')
#=> ["notone", " nottwo"]
b = a.map { |s| s.gsub(/#|not|\s+/,"") }
#=> ["one", "two"]
Because I used Enumerable#flat_map, "one" and "two" are added to the array being returned. When str #=> "notCAPSLOCK",
s = str.downcase
#=> "notcapslock"
a = s.split(',')
#=> ["notcapslock"]
b = a.map { |s| s.gsub(/#|not|\s+/,"") }
#=> ["capslock"]
Here is one more solution that uses a different technique of capturing what you want rather than dropping what you don't want: (for the most part)
a = ["not12345", " notabc", "notone, nottwo",
"notCAPSLOCK", "##doublehash:","h#a#s#h", "#notswaggerest"]
a.map do |s|
s.downcase.delete("#").scan(/(?<=not)\w+|^[^not]\w+/)
end
#=> [["12345"], ["abc"], ["one", "two"], ["capslock"], ["doublehash"], ["hash"], ["swaggerest"]]
Had to delete the # because of h#a#s#h otherwise delete could have been avoided with a regex like /(?<=not|^#[^not])\w+/
You can use this regex to solve your problem. I tested and it works for all of your test cases.
/^\s*#*(not)*/
^ means match start of string
\s* matches any space at the start
#* matches 0 or more #
(not)* matches the phrase "not" zero or more times.
Note: this regex won't work for cases where "not" comes before "#", such as not#hash would return #hash
Fun problem because it can use the most common string functions in Ruby:
result = values.map do |string|
string.strip # Remove spaces in front and back.
.tr('#','') # Transform single characters. In this case remove #
.gsub('not','') # Substitute patterns
.split(', ') # Split into arrays.
end
p result #=>[["12345"], ["abc"], ["one", "two"], ["CAPSLOCK"], ["doublehash"], ["hash"], ["swaggerest"]]
I prefer this way rather than a regexp as it is easy to understand the logic of each line.
Ruby regular expressions allow comments, so to match the octothorpe (#) you can escape it:
"#foo".sub(/\#/, "") #=> "foo"
I can't tell what's wrong with my code:
def morse_code(str)
string = []
string.push(str.split(' '))
puts string
puts string[2]
end
What I'm expecting is if I use "what is the dog" for str, I would get the following results:
=> ["what", "is", "the", "dog"]
=> "the"
But what I get instead is nil. If I do string[0], it just gives me the entire string again. Does the .split function not break them up into different elements? If anyone could help, that would be great. Thank you for taking the time to read this.
Your code should be :
def morse_code(str)
string = []
string.push(*str.split(' '))
puts string
p string[2]
end
morse_code("what is the dog" )
# >> what
# >> is
# >> the
# >> dog
# >> "the"
str.split(' ') is giving ["what", "is", "the", "dog"], and you are pushing this array object to the array string. Thus string became [["what", "is", "the", "dog"]]. Thus string is an array of size 1. Thus if you want to access any index like 1, 2 so on.., you will get nil. You can debug it using p(it calls #inspect on the array), BUT NOT puts.
def morse_code(str)
string = []
string.push(str.split(' '))
p string
end
morse_code("what is the dog" )
# >> [["what", "is", "the", "dog"]]
With Array, puts works completely different way than p. I am not good to read MRI code always, thus I take a look at sometime Rubinious code. Look how they defined IO::puts, which is same as MRI. Now look the specs for the code
it "flattens a nested array before writing it" do
#io.should_receive(:write).with("1")
#io.should_receive(:write).with("2")
#io.should_receive(:write).with("3")
#io.should_receive(:write).with("\n").exactly(3).times
#io.puts([1, 2, [3]]).should == nil
end
it "writes nothing for an empty array" do
x = []
#io.should_receive(:write).exactly(0).times
#io.puts(x).should == nil
end
it "writes [...] for a recursive array arg" do
x = []
x << 2 << x
#io.should_receive(:write).with("2")
#io.should_receive(:write).with("[...]")
#io.should_receive(:write).with("\n").exactly(2).times
#io.puts(x).should == nil
end
We can now be sure that, IO::puts or Kernel::puts behaves with array just the way, as Rubinious people implemented it. You can now take a look at the MRI code also. I just found the MRI one, look the below test
def test_puts_recursive_array
a = ["foo"]
a << a
pipe(proc do |w|
w.puts a
w.close
end, proc do |r|
assert_equal("foo\n[...]\n", r.read)
end)
end
I'd like to replace/duplicate a substring, between two delimeters -- e.g.,:
"This is (the string) I want to replace"
I'd like to strip out everything between the characters ( and ), and set that substr to a variable -- is there a built in function to do this?
I would just do:
my_string = "This is (the string) I want to replace"
p my_string.split(/[()]/) #=> ["This is ", "the string", " I want to replace"]
p my_string.split(/[()]/)[1] #=> "the string"
Here are two more ways to do it:
/\((?<inside_parenthesis>.*?)\)/ =~ my_string
p inside_parenthesis #=> "the string"
my_new_var = my_string[/\((.*?)\)/,1]
p my_new_var #=> "the string"
Edit - Examples to explain the last method:
my_string = 'hello there'
capture = /h(e)(ll)o/
p my_string[capture] #=> "hello"
p my_string[capture, 1] #=> "e"
p my_string[capture, 2] #=> "ll"
var = "This is (the string) I want to replace"[/(?<=\()[^)]*(?=\))/]
var # => "the string"
str = "This is (the string) I want to replace"
str.match(/\((.*)\)/)
some_var = $1 # => "the string"
As I understand, you want to remove or replace a substring as well as set a variable equal to that substring (sans the parentheses). There are many ways to do this, some of which are slight variants of the other answers. Here's another way that also allows for the possibility of multiple substrings within parentheses, picking up from #sawa's comments:
def doit(str, repl)
vars = []
str.gsub(/\(.*?\)/) {|m| vars << m[1..-2]; repl}, vars
end
new_str, vars = doit("This is (the string) I want to replace", '')
new_str # => => "This is I want to replace"
vars # => ["the string"]
new_str, vars = doit("This is (the string) I (really) want (to replace)", '')
new_str # => "This is I want"
vars # => ["the string", "really, "to replace"]
new_str, vars = doit("This (short) string is a () keeper", "hot dang")
new_str # => "This hot dang string is a hot dang keeper"
vars # => ["short", ""]
In the regex, the ? in .*? makes .* "lazy". gsub passes each match m to the block; the block strips the parens and adds it to vars, then returns the replacement string. This regex also works:
/\([^\(]*\)/
Why isnt that working:
>> s = "hi"
=> "hi"
>> s == ("hi"|"ho")
NoMethodError: undefined method `|' for "hi":String
from (irb):2
>>
I don't get it.. Is there a solution for this kind of syntax? Because
s == ("hi"|"ho")
#is shorther than
s == "hi" || s == "ho"
Yes, the bitwise operator | is not defined in the String class: http://ruby-doc.org/core/classes/String.html
Consider this for expressiveness:
["hi", "ho"].include? myStr
irb(main):001:0> s = "hi"
=> "hi"
irb(main):002:0> ["hi", "ho"]
=> ["hi", "ho"]
irb(main):003:0> ["hi", "ho"].include? s
=> true
irb(main):004:0> s = "foo"
=> "foo"
irb(main):005:0> ["hi", "ho"].include? s
=> false
In most high level languages that syntax will not work, you have to stick to the longer syntax of:
s == "hi" || s == "ho"
Note that | is a bitwise or, whereas || is a regular or
You could use the include? method on array if you've got several == tests to do:
["hi", "ho"].include?(s)
Not shorter for two checks admittedly but it will be shorter for three or more.
This syntax doesn't exist in any language as far as I know.
What you are saying
s == ("hi"|"ho")
Literally translates to 'bitwise OR the strings "hi" and "ho" together and then compare them with s'. If you can't see why this is not what you are looking for, try writing down the ASCII codes for "hi" and "ho" and then bitwise ORing them together. You are going to get complete gibberish.
You could make it work that way:
irb> class Pair
def initialize(strA,strB)
#strA,#strB = strA,strB
end
def ==(string)
string == #strA || string == #strB
end
def |(other)
Pair.new(self,other)
end
end
#=> nil
irb> class String
def |(other)
Pair.new(self,other)
end
alias old_equals :==
def ==(other)
if other.kind_of? Pair
other == self
else
old_equals other
end
end
end
#=> nil
irb> ("one"|"two") == "one"
#=> true
irb> ("one"|"two") == "two"
#=> true
irb> ("one"|"two") == "three"
#=> false
irb> "one" == ("one"|"two")
#=> true
irb> "three" == ("one"|"two"|"three")
#=> true
But since this involves some monkey-patching of a fairly lowlevel class, I wouldn't advise relying on it. Other people will hate reading your code.
Ruby supports binary 'or' and other binary operations on values of type Fixnum and Bignum, meaning any integer. Bitwise operations aren't supported on strings or any other type, as far as I know.
As other people have mentioned, you probably want something other than binary operations altogether. However, you can easily get integer representations of characters, so you can compare characters like so:
a = "Cake"
b = "Pie"
puts a[0] | b[0] # Prints "83" - C is 67 and P is 80.
You can get an array of the comparisons easily with some conversions.
a = "Cake"
b = "Pie " # Strings of uneven length is trivial but more cluttered.
a_arr = a.split(//)
b_arr = b.split(//)
c_arr = []
a.each_with_index { |char, i| c.push(a[i].to_i | b[i].to_i) }
# If you *really* want an ASCII string back...
c = c_arr.collect(&:chr).join
You could use a regex:
Like so:
regex = /hi|ho/
s = "hi"
t = "foo"
s =~ regex
#=> 0
t =~ regex
#=> nil