Perform an opeartion within a hash - ruby

I want to perform a block operation on a hash without an iteration, smth like follows:
myhash = {
foo: 'foo',
bar: 'bar',
baz: 'baz'
}
with myhash do
# operation with foo:
# operation with bar:
# other operations, etc
# operation with baz:
end
to avoid the syntax repeating:
myhash[:foo]
myhash[:bar]
myhash[:baz]
# much mo keys
Is that possible?

Even though as many suggested it's probably better to just stick with myhash[:foo], it's possible to do what you want, but only for science sake!
require 'ostruct'
myhash = OpenStruct.new({
foo: 'foo',
bar: 'bar',
baz: 'baz'
})
# you can do stuff like this with ostruct
myhash.foo
myhash.bar
#thing = "urgh"
thing = #thing
# every object has a built in tap method
myhash.tap { |h|
p #thing
h.foo + h.bar
}
# evey object has the instance_eval method
# when using instance eval, there are some trade-off
myhash.instance_eval do
# instance variables don't work as you'd expect in there
p #thing
# but variables and methods do!
p thing
derp = 4
p (foo * derp)
p (baz + foo * derp)
end
Sorry the output is a mess, but you didn't specify what the output had to look like :)

There is no way to create local variables in Ruby (1.9+); so you are stuck with using Hash, OpenStruct or Struct. ("Convert a Hash into a Struct") Just use the Hash, it is not that big a deal.

Not really sure if it is useful, I would just use the hash syntax, but you could do something like
foo, bar, baz = myhash.values_at(:foo, :bar, :baz)
or convert it to an OpenStruct :
h = OpenStruct.new(myhash)
and then you can just write
h.foo = h.bar + h.baz

You could do something like this:
my_dogs = { dog1: "Bobo", dog2: "Millie", dog3: "Billy-Bob", dog4: "Erma" }
flea_ridden = my_dogs.values_at(:dog3, :dog1, :dog4)
#=> ["Billy-Bob", "Bobo", "Erma"]
str = "My dogs "
#=> "My dogs "
str << "#{flea_ridden.shift}, "
#=> "My dogs Billy-Bob, "
str << "#{flea_ridden.shift}, "
#=> "My dogs Billy-Bob, Bobo, "
str << "and "
#=> "My dogs Billy-Bob, Bobo, and "
str << "#{flea_ridden.shift} "
#=> "My dogs Billy-Bob, Bobo, and Erma "
str << "have fleas"
#=> "My dogs Billy-Bob, Bobo, and Erma have fleas"
puts str
# My dogs Billy-Bob, Bobo, and Erma have fleas

Related

How to run a loop on an array, combine the results and set them to a variable

I have this array: ['John', 'Michael', 'Siri']. How can I run an each loop on them and add a text to each "part" of the array and at the end "combine" the result of the loops and set/assign the result of all each to a variable?
By that I mean, I do:
array = ['John', 'Michael', 'Siri']
array.each do |a|
text = "#{a} here"
# Results would need be =>
# John is here
# Michael is here
# Siri is here
#new_string = text # => Which would need to be "John is here Michael is here Siri is here"
end
I have done the code above, but #new_string becomes only Siri is here and if I move the #string out of the loop, like below, it becomes John is here, so basically it takes only one of them and "assigns" it to #new_string.
array = ['John', 'Michael', 'Siri']
array.each do |a|
#text = "#{a} here"
end
#new_string = #text
I tested with [0]+[1]+[2] and it kind of worked, but the problem is that I would not know the size of my array. It can be 2 items or it can be 100 items.
This way
array = ["John", "Michael", "Siri"]
your_variable = array.map { |name| "#{name} is here" }.join(" ")
It's basically a transformation, you want to add something to each element of a collection (use map for that). Lastly, join them up.
Can be done by
array.map { |x| x + ' is here' }.join(' ')
More concisely:
%w{ John Michael Siri}.collect{|s| s+" is here"}.join(" ")
Given the array = ['John', 'Michael', 'Siri'], the problem with your code is that the variable has a scope inside of the loop so it is not accessible after the loop ends.
The solution is to declare the variable before.
#new_string = '' # initialize outside the loop
array.each do |a|
text = "#{a} here "
#new_string += text # note +=
end
#new_string #=> "John here Michael here Siri here "
For the second code, the problem is the same:
#new_string = '' # initialize outside the loop
array = ['John', 'Michael', 'Siri']
array.each do |a|
#new_string += "#{a} here " # note +=
end
#new_string #=> "John here Michael here Siri here "
As you can see string ends with a space, to avoid it populate an array then join as showed in previous answers:
#new_string = [] # initialize outside the loop
array = ['John', 'Michael', 'Siri']
array.each do |a|
#new_string << "#{a} here" # note +=
end
p #new_string = #new_string.join(' ') #=> "John here Michael here Siri here"
Side note:
# comments in ruby starts with #

Replace matched lines in a file but ignore commented-out lines using Ruby

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')

Ruby string pass by reference function parameter

Ruby noob here
I understand ruby does pass by reference for function parameters
However, I am getting the feeling this is slightly different from conventional c/c++ style pass by reference
Sample code:
def test1(str)
str += ' World!'
end
def test2(str)
str << ' World!'
end
str = 'Hello'
test1(str)
p str # Hello
test2(str)
p str # Hello World!
I would expect test1 to also return Hello World! if I were using references in c/c++.
This is simply out of curiosity -- any explanations would be appreciated
I understand ruby does pass by reference for function parameters
Ruby is strictly pass-by-value, always. There is no pass-by-reference in Ruby, ever.
This is simply out of curiosity -- any explanations would be appreciated
The simple explanation for why your code snippet doesn't show the result you would expect for pass-by-reference is that Ruby isn't pass-by-reference. It is pass-by-value, and your code snippet proves that.
Here is a small snippet that demonstrates that Ruby is, in fact, pass-by-value and not pass-by-reference:
#!/usr/bin/env ruby
def is_ruby_pass_by_value?(foo)
foo << <<~HERE
More precisely, it is call-by-object-sharing!
Call-by-object-sharing is a special case of pass-by-value,
where the value is always an immutable pointer to a (potentially mutable) value.
HERE
foo = 'No, Ruby is pass-by-reference.'
return
end
bar = ['Yes, of course, Ruby *is* pass-by-value!']
is_ruby_pass_by_value?(bar)
puts bar
# Yes, of course, Ruby *is* pass-by-value!,
# More precisely, it is call-by-object-sharing!
# Call-by-object-sharing is a special case of pass-by-value,
# where the value is always an immutable pointer to a (potentially mutable) value.
Ruby does however allow mutation of objects, it is not a purely functional language like Haskell or Clean.
In the first case a new object was created when you did str += ' World!'
str = "Hello"
=> "Hello"
str.object_id
=> 69867706917360
str += " World"
=> "Hello World"
str.object_id
=> 69867706885680
str = "Hello"
=> "Hello"
str.object_id
=> 69867706856200
str << " World"
=> "Hello World"
str.object_id
=> 69867706856200
str = "Hello"
=> "Hello"
str.object_id
=> 69867706786780
str.freeze
=> "Hello"
str << " World"
RuntimeError: can't modify frozen String
str += " World"
=> "Hello World"
"<<" is a Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.
So "<<" doesn't create a new string, str.contact("World") doesn't create a new string as well.
The method test1 doesn't have to do anything with the returned result , you can try this method :
def test1(str)
str.concat(' World!')
end
Look at the following adaption of your test, by showing the object_id of your object you can easily see if it is the same or not. Test1 returns another String object because of the += concatenation but it is not used afterward.
This looks like passed by reference but in reality it is the value of the pointer to the object that is passed. The best explenation I could find for this is here , the author calls it pass-reference-by-value
def test1(str)
p ["in test1 before", str.object_id]
str += ' World!'
p ["in test1 after", str.object_id]
str
end
def test2(str)
p ["in test2", str.object_id]
str << ' World!'
end
str = 'Hello'
p ["in main", str.object_id]
test1(str)
p str # Hello
p ["after test1", str.object_id]
test2(str)
p str
p ["after test2", str.object_id]
Gives
["in main", 12363600]
["in test1 before", 12363600] # the same object, so pointer to object passed by value
["in test1 after", 12362976] # returns a new object, the old is unchanged
"Hello"
["after test1", 12363600] # idem
["in test2", 12363600]
"Hello World!"
["after test2", 12363600]
# still the same object
STRING IS REFERENCED, in ruby except value like numbers, true, false, nil, others are referenced.
a = "hello"
b = a
a.replace("Hola")
p a # Hola
p b # Hola
You would wanna add the magic comment at the beginning.:
# frozen_string_literal: true
a = "hello"
b = a
a.replace("Hola") # can't modify frozen String: "hello" (FrozenError)
def test1(str)
str += ' World!'
end
operator += is a syntactic sugar in ruby. Expression a += b translates to a = a + b. Operator + applied on String instance creates new String, which is a concatenation of the two arguments. That is why str is not modified in the first case.
Also I'd like to correct your statement:
I understand ruby does pass by reference for function parameters
Actually ruby passes by reference every parameter except the "value types" - ie. values nil, true, false and instances of class Fixnum

Replace characters from string without changing its object_id in Ruby

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

Difference between << and =

Can anyone explain why foo is mutated in version 1? What is the difference between << and = assignment?
VERSION 1
foo = "apple"
bar = foo
"foo: #{foo}" # => foo: apple
bar << "hello"
"bar: #{bar}" # => bar: applehello
"foo: #{foo}" # => foo: applehello
VERSION2
foo = "apple"
bar = foo
"foo: #{foo}" # => foo: apple
bar = bar + "hello"
"bar: #{bar}" # => bar: applehello
"foo: #{foo}" # => foo: apple
Because = is an assignment as you said.
But << is not an assignment - it's concatenation operator when the left operand is a string.
So:
bar = bar + "hello"
creates a new string by joining contents of bar with "hello" and then this new string is assigned to variable bar, while:
bar << "hello"
does the in-place concatenation of string - bar won't be set to new string but the string it holds will be modified.
So with << bar and foo still keep reference to the same object while with = only bar gets a new value.
You're setting bar as a reference to foo. The << operator works in place, as in the first version, and in the second version you're using + which produces a new value, while not changing the original.
bar << "hello" appends to bar (which is foo), while bar = bar + "hello" creates a copy of the string, foo remains untouched.
String concatenation with + returns a new object:
http://www.ruby-doc.org/core-2.1.0/String.html#method-i-2B
The append operator acts on the object that the reference points to:
http://www.ruby-doc.org/core-2.1.0/String.html#method-i-3C-3C
In the first example you are appending to the object that both foo and bar point to.
In the second example you are adding "hello" to the object that bar points to which returns a new object which, in turn, bar now points to all the while foo still points to the object whose value is still just "apple"
First observe the following:--
str = "test"
#=> "test"
str[1]
#=> "e"
str1 = str
#=> "test"
str.object_id
#=> 8509820
str1.object_id
#=> 8509820
So string is stored as an array of each character in Ruby. Other language like Java also returns complete string if you just use char type reference. Similarly here also we get each char of second string added to array of characters for first string.
str << "string"
#=> "teststring"
str1
#=> "teststring"
str.object_id
#=> 8509820
str1.object_id
#=> 8509820
Here no new object gets created. Same array holds each characters of second string.
Now observe the following:--
str = "test"
#=> "test"
str1 = str
str.object_id
#=> 9812345
str1.object_id
#=> 9812345
str = str + "string"
#=> "teststring"
str.object_id
#=> 9901234
str1
#=> "test"
str1.object_id
#=> 9812345
Here we see + operator causes creation of a new object.

Resources