I have a class that contains an array. This is an example class. a is an array
class Holder
attr_accessor :a
end
I am trying to make a copy of an object and execute a function on its array. An example situation:
t = Holder.new
t.a = (1..9).to_a
t2= Holder.new
t2.a = t.a
t2.a[2]+=10
t2.a
# => [1, 2, 13, 4, 5, 6, 7, 8, 9]
t.a
# => [1, 2, 13, 4, 5, 6, 7, 8, 9]
Both array in each object are effected. I don't know how to make them separate. I tried with clone and dup too.
dupt = t2.dup
dupt.a[8]+=10
dupt
# => #<Holder:0x007fb6e193b0a8 #a=[1, 2, 13, 4, 5, 6, 7, 8, 19]>
t2
# => #<Holder:0x007fb6e1962ba8 #a=[1, 2, 13, 4, 5, 6, 7, 8, 19]>
You need to call dup on the Array, not on your Holder object. dup will not create copies of all the sub-elements in the object you are trying to copy.
t2.a = t.a.dup
Related
If I have an array I can create a set (unique values) from it with
require 'set'
s = [11,12,3,2,3,4,3,5,89,1,2,3,4]
uniq_s = s.to_set # gives [11,12,3,2,4,5,89,1]
If I want the result to be sorted I could do this with
sorted_s = s.sort
How could I also do this using a SortedSet ?
I tried using array.to_sorted_set but that doesn't exist
Pass the existing set to SortedSet.new
Example:
irb(main):046:0> s = [11,12,3,2,3,4,3,5,89,1,2,3,4].to_set
=> #<Set: {11, 12, 3, 2, 4, 5, 89, 1}>
irb(main):047:0> sorted_s = SortedSet.new(s)
=> #<SortedSet: {1, 2, 3, 4, 5, 11, 12, 89}>
irb(main):048:0>
You can also pass the set class as described in the #to_set documentation.
require 'set'
s = [11,12,3,2,3,4,3,5,89,1,2,3,4]
s.to_set(SortedSet)
#=> #<SortedSet: {1, 2, 3, 4, 5, 11, 12, 89}>
I'm using the below code to generate a set from a range:
my_set = *(1..10).to_set
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
As you can see, instead of getting a set, I get an array.
The statement works if I split it into two lines:
my_set = *(1..10)
my_set = my_set.to_set
# => #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
How can I get it to work as expected in one line?
You already had a set. Splatting it (*) gave you the array. Just don't splat:
my_set = (1..10).to_set # => #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Set.new works as well with ranges:
Set.new(1..10)
=> #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Just for fun, if you want to use splat operator (*):
my_set = (_ = *(1..10)).to_set
=> #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Note, that you should use assignment if you want to splat range.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
def mergeArrays (ar1, ar2)
result = (ar1 << ar2).flatten!
require 'pp'
pp %w(result)
end
As simple as this. I am trying to merge these two arrays and display the result. I am also brand-brand new to Ruby. This is the first function I am writing in this language. Trying to learn here. Also how can I remove the duplicates?
It would help if you give example inputs and outputs so we know exactly what you want. When you use the word "merge", I think you actually just want to add the arrays together:
ar1 = [1, 2, 3]
ar2 = [3, 4, 5]
ar3 = ar1 + ar2 # => [1, 2, 3, 3, 4, 5]
Now if you want to remove duplicates, use Array#uniq:
ar4 = ar3.uniq # => [1, 2, 3, 4, 5]
There is no need to write a method to do any of this since the Ruby Array class already supports it. You should skim through the documentation of the Array class to learn more things you can do with arrays.
What do you mean 'not working'?
Similar questions have been asked here:
Array Merge (Union)
You have two options: the pipe operator (a1 | a2) or concatenate-and-uniq ((a1 + a2).uniq).
Also be careful about using <<, this will modify the original variable, concatenating ar2 onto the end of the original ar1.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
result = (nums1<< nums2).flatten!
nums1
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
nums2
=> [5, 6, 7, 8, 9]
result
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
Additionally- just another Ruby tip, you do not need the destructive flatten! with ! versus the regular flatten. The regular flatten method will return a new Array, which you assign to result in your case. flatten! will flatten self in place, altering whatever Array it's called upon, rather than returning a new array.
You can merge Arrays using '+' operator and you can ignore the duplicated values using .uniq
>> nums1 = Array[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> nums2 = Array[5, 6, 7, 8, 9]
=> [5, 6, 7, 8, 9]
>> def mergeArrays (nums1, nums2)
>> result = (nums1 + nums2).uniq
>> end
=> :mergeArrays
>> mergeArrays(nums1,nums2)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
p nums1.concat(nums2).uniq
I know the idiomatic way to do a for loop in Ruby is to use an Enumerator like .each, but I'm running into a problem: I'd like to iterate over a subset of an Array and modify those elements. Calling .map! with a subset like ary[0..2] or .slice(0..2) doesn't seem to do it; presumably because that slicing operator is creating a new Array?
Desired behavior with for instead of iterator:
iter_ind = [2,3,4]
my_ary = [1,3,5,7,9,11]
for j in iter_ind
my_ary[j] = my_ary[j] + 1
# some other stuff like an exchange operation maybe
end
=> [1, 3, 6, 8, 10, 11]
Things that don't work:
irb(main):032:0> ar[2..4].map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):033:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):034:0> ar.slice(2..4).map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):035:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):036:0> ar[2..4].collect! {|el| el = el+1}
=> [6, 8, 10]
irb(main):037:0> ar
=> [1, 3, 5, 7, 9, 11]
Try this.
In example below I implemented something that could be named map_with_index. each_with_index if no block given returns iterator. I use it to map our array.
ary = [1, 3, 5, 7, 9, 11]
ary.each_with_index.map { |elem, index| index.between?(2, 4) ? elem += 1 : elem }
# => [1, 3, 6, 8, 10, 11]
You may also try the following:
?> ary = [1, 3, 5, 7, 9, 11]
=> [1, 3, 5, 7, 9, 11]
?> ary.map!.with_index {|item, index| index.between?(2, 4) ? item += 1 : item}
=> [1, 3, 6, 8, 10, 11]
?> ary
=> [1, 3, 6, 8, 10, 11]
You could use Array#each_index if you don't mind referencing the array twice:
ary = [1, 3, 5, 7, 9, 11]
ary.each_index { |i| ary[i] += 1 if i.between? 2, 4 }
#=> [1, 3, 6, 8, 10, 11]
Or if you don't want to iterate the whole array, this would work, too:
ary = [1, 3, 5, 7, 9, 11]
ary[2..4] = ary[2..4].map { |el| el + 1 }
ary
#=> [1, 3, 6, 8, 10, 11]
What is an easy way to generate an array that has values with a fixed distance between them?
For example:
1, 4, 7, 10,... etc
I need to be able to set start, end, and step distance.
Try using Range.step:
> (1..19).step(3).to_a
=> [1, 4, 7, 10, 13, 16, 19]
In Ruby 1.9:
1.step(12).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
1.step(12,3).to_a #=> [1, 4, 7, 10]
Or you can splat instead of to_a:
a = *1.step(12,3) #=> [1, 4, 7, 10]