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
Related
As you may know that in Ruby two same strings do not have a same object_id, while two same symbols do. For instance:
irb(main):001:0> :george.object_id == :george.object_id
=> true
irb(main):002:0> "george".object_id == "george".object_id
=> false
However, in my code below, it shows that two strings which have a same value "one" having a same object_id.
class MyArray < Array
def ==(x)
comparison = Array.new()
x.each_with_index{|item, i| comparison.push(item.object_id.equal?(self[i].object_id))}
if comparison.include?(false) then
false
else
true
end
end
end
class MyHash < Hash
def ==(x)
y = Hash[self.sort]
puts y.class
puts y
x = Hash[x.sort]
puts x.class
puts x
puts "______"
xkeys = MyArray.new(x.keys)
puts xkeys.class
puts xkeys.to_s
puts xkeys.object_id
puts xkeys[0].class
puts xkeys[0]
puts xkeys[0].object_id
puts "______"
xvals = MyArray.new(x.values)
puts "______"
selfkeys = MyArray.new(y.keys)
puts selfkeys.class
puts selfkeys.to_s
puts selfkeys.object_id
puts selfkeys[0].class
puts selfkeys[0]
puts selfkeys[0].object_id
puts "______"
selfvals = MyArray.new(y.values)
puts xkeys.==(selfkeys)
puts xvals.==(selfvals)
end
end
a1 = MyHash[{"one" => 1, "two" => 2}]
b1 = MyHash[{"one" => 1, "two" => 2}]
puts a1.==(b1)
And Get
Hash
{"one"=>1, "two"=>2}
Hash
{"one"=>1, "two"=>2}
______
MyArray
["one", "two"]
21638020
String
one
21641920
______
______
MyArray
["one", "two"]
21637580
String
one
21641920
______
true
true
As you can see from the result that 2 String objects with have a same value "one" having a same object_id 21641920, while it's supposed to have different ID. So can anyone give me some hints or tell me how can I get different ID in this case?
Best Regards.
When a String object is used as a key in a Hash, the hash will duplicate and freeze the string internally and will use that copy as its key.
Reference: Hash#store.
As of ruby 2.2 strings used as keys in hash literals are frozen and de-duplicated: the same string will be reused.
This is a performance optimisation: not allocating many copies of the same string means there are fewer objects to allocate and fewer to garbage collect.
Another way to see frozen string literals in action :
"foo".freeze.object_id == "foo".freeze.object_id
Will return true in versions of ruby >= 2.1
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
What's the most-efficient manner to remove beginning and ending spaces around a string, then convert the string to nil if the resulting value is zero-length?
For example:
> a=''
> squash(a)
=> nil
> a=' '
> squash(a)
=> nil
> a=' xyz '
> squash(a)
=> 'xyz'
> a=nil
> squash(a)
=> nil
Thus far:
def squash(value)
return nil if value.nil?
value.strip!
(value.blank? ? nil : value)
end
Seems like there could be a more-terse way of implementing this.
** edit **
While I am working in Rails, it would be nice if the answer would contain a Ruby-only implementation, too.
I should emphasize that the implementation needs to be able to handle a string with a nil value.
Assuming you want this for rails (otherwise blank? is undefined) you can use presence method:
def squash(value)
value && value.strip.presence
end
In pure ruby, I would do:
def squash(value)
return unless value
value = value.strip
value unless value.empty?
end
This will work with plain Ruby:
def squash(str)
str = str.to_s.strip
str unless str.empty?
end
Here's one way:
def squash(str)
(str && str[/\S/]) ? str.strip : nil
end
/\S/ looks for a character that is not whitespace.
squash " My dog has fleas. " #=> "My dog has fleas."
squash " " #=> nil
squash nil #=> nil
Reader challenge
I tried to also implement squash!, that would convert the argument str in place. If str is nil, just leave it alone. If str contains a least one non-whitespace character, then str.strip!. However, I could not figure out a way to convert a string to nil. I wanted to do this when the string is empty or contains only whitespace, but the problem is to convert any string, or more generally, any non-nil object, to nil, when the object is received as a method argument. Can it be done? [Edit: #Stefan says the type cannot be changed. I'm sure he's right, but I would like to see where that is written and understand why it is not permitted. Anyone? tidE].
This handles all your examples.
def squash(value)
value.to_s.strip.empty? ? nil : value.strip
end
Just adding this because it's short:
def squash(str)
str.to_s[/\S(.*\S)?/]
end
squash(nil) #=> nil
squash("") #=> nil
squash(" ") #=> nil
squash("a") #=> "a"
squash(" a") #=> "a"
squash("a ") #=> "a"
squash(" a ") #=> "a"
squash(" foo ") #=> "foo"
squash(" foo bar ") #=> "foo bar"
Here's a plain ruby version:
def squash(str)
str && str.strip! && (str unless str.empty?)
end
Update - If you want a version without side effects:
def squash(str)
str && (x = str.strip) && (x unless x.empty?)
end
If you want the method name squash with argument value.
def squash(value)
return value unless value.instance_of?(String)
return if value.strip!&.empty?
value
end
Features:
Works either on pure Ruby or Ruby on Rails
Works with other data types than string as well, for example, you can pass a number if you want
Testing:
squash('ok')
#=> ok
squash('')
#=> nil
squash(' ')
#=> nil
squash(' xyz ')
#=> 'xyz'
squash('xyz ')
#=> 'xyz'
squash(' xyz')
#=> 'xyz'
squash(123)
#=> 123
squash(nil)
#=> nil
Note:
I use safe navigation operator, which was released in Ruby 2.3.0. So make sure before using it.
irb(main):001:0> s = " string "
=> " string "
irb(main):002:0> s.strip!
=> "string"
irb(main):003:0> s.blank?
NoMethodError: undefined method `blank?' for "string":String
from (irb):3
from C:/RUBY/BIN/irb:12:in `'
irb(main):004:0>
I think blank is not Ruby but Rails? Anyway, what's wrong with
(value.length == 0 ? nil : value)
or even better
value.empty? ? nil : value
At least everybody would understand what the intention is here.
We can easily convert a keyword into a string:
true.to_s
=> "true"
But how to convert a string into a keyword?
How many keywords do you have? What's your definition of a 'keyword'?
I would implement with a case-command. You may define a to_keyword method for String. My implementation detects true, false, nil (or NULL). The strings are detected, ignoring capitals (TRUE will also be true) Other strings will return a symbol (The string itself would be another reasonable result).
The example can be adapted for further 'keywords' or other results.
class String
#Return 'keyword'
#Detects:
#- true (independend of lower letters/capitals)
#- false (independend of lower letters/capitals)
#- nil/NULL (independend of lower letters/capitals)
def to_keyword
case self
when /\Atrue\Z/i; true
when /\Afalse\Z/i; false
when /\Anil\Z/i, /\ANULL\Z/; nil
else; self.to_sym #return symbol. Other posibility: self.
end
end
end
p 'true'.to_keyword #true
p 'TRUE'.to_keyword #true
p 'false'.to_keyword #false
p 'NULL'.to_keyword #nil (NULL is used in DB like nil)
p 'NULLc'.to_keyword #:NULLc not detected -> symbol
try this:
ruby-1.9.2-p136 :001 > true
=> true
ruby-1.9.2-p136 :002 > eval("true")
=> true
You could try yaml:
require "yaml"
p YAML.load('true')
p YAML.load('TRUE')
p YAML.load('false')
p YAML.load('nil')
p YAML.load('NULL') #nil
I like Knut's answers mostly. I don't think I would support "Null" and others though.
Here is this version which is a little more simple.
class String
def to_keyword
self == "true"
end
end
>> "true".to_keyword
=> true
>> "false".to_keyword
=> false
This problem is pretty straight forward though. In your tests you could simply
:correct => (true_string == "true")
Following your comment, you could do something like that :
true_string = "true"
:correct => !!true_string
# examples
!true_string #=> false
!!true_string #=> true
!!!true_string #=> false
...
In SO question 2068165 one answer raised the idea of using something like this:
params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])
as a DRYer way of saying
params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]
where the params Hash would be coming from a (Rails/ActionView) form.
It's a kind of corollary to the well-known ||= idiom, setting the value if the LHS is not nil/false.
Is using &&= like this actually a recognised Ruby idiom that I've somehow missed or have I just forgotten a more commonly-used idiom? It is getting rather late...
It ought to be. If nothing else, params[:task] is only evaluated once when using the &&= form.
To clarify:
params[:task][:completed_at] = params[:task][:completed_at] && ...
calls [](:task) on params twice, [](:completed_at) and []=(:completed_at) once each on params[:task].
params[:task][:completed_at] &&= ...
calls [](:task) on params once, and its value is stashed away for both the [](:completed_at) and []=(:completed_at) calls.
Actual example describing what I'm trying to illustrate (based on Marc-Andre's example code; much thanks):
class X
def get
puts "get"
#hash ||= {}
end
end
irb(main):008:0> x = X.new
=> #<X:0x7f43c496b130>
irb(main):009:0> x.get
get
=> {}
irb(main):010:0> x.get[:foo] = 'foo'
get
=> "foo"
irb(main):011:0> x.get[:foo]
get
=> "foo"
irb(main):012:0> x.get[:foo] &&= 'bar'
get
=> "bar"
irb(main):013:0> x.get[:foo] = x.get[:foo] && 'bar'
get
get
=> "bar"
Note that using the "expanded" form causes "get" to be printed out twice, but using the compact form causes it to only be printed once.
Using &&=, in the case of LHS is false, it is only being read once, but not being set. This should make it clearer ...
class Test
def initialize(value)
#v = value
end
def v=(value)
puts "set"
#v = value
end
def v
puts "get=>#{#v}"
#v
end
end
t = Test.new(true)
t.v = t.v && true
puts '----'
t.v &&= true
puts '----'
t = Test.new(false) # lets make LHS false
t.v = t.v && true
puts '----'
t = Test.new(false) # lets make LHS false
t.v &&= true
The result:
get=>true
set
----
get=>true
set
----
get=>false
set
----
get=>false