How to remember swapped array elements in another array? - ruby

This is driving me crazy! I've been trying to write a Ruby method to find all permutations, to solve Project Euler's problem 24. When I swap the elements of an array, they are swapped properly. But when I try to STORE this swapped array in a DIFFERENT array, this new array only remembers the latest copy of my swapped array! It won't remember the older version.
When I print out a during the loop, it shows all permutations properly. But when I print out perm (which I use to store all different permutations of a), it only shows 1 version of a repeated several times. How do I fix this?
a = [0, 1, 2, 3]
perms = []
p "a = #{a}" # output: "a = [0, 1, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # output: "perms = [[0, 1, 2, 3]]"
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 0, 2, 3], [1, 0, 2, 3]]"
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 2, 0, 3], [1, 2, 0, 3], [1, 2, 0, 3]]"
Thanks to Sawa below, both "dup" and "clone" methods solved my problem! Why doesn't my original way work? When would I use "dup" vs. "clone"? Please give me some code examples.
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3]]" *** it remembers!
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]"

Variables in Ruby (with some exceptions, such as variables bound to integers) contain references to objects, not values. Here's an example from running "irb":
1.9.3p374 :021 > str1="hi"
=> "hi"
1.9.3p374 :022 > str2=str1
=> "hi"
1.9.3p374 :023 > str1.replace("world")
=> "world"
1.9.3p374 :024 > str2
=> "world"
You'll notice that once I replace the value for str1, str2's "value" changes as well. That's because it contains a reference to the str1 object. I know one difference between dup and clone has to do with the "freeze" method. If I had called str1.freeze, then it would prevent the object str1 references from being modified, e.g.,
1.9.3p374 :055 > str1.freeze
=> "hi"
1.9.3p374 :056 > str1[0]="b"
RuntimeError: can't modify frozen String
from (irb):56:in `[]='
from (irb):56
from /.rvm/rubies/ruby-1.9.3-p374/bin/irb:13:in `<main>
"Dup"-ing a frozen object doesn't create a frozen object whereas cloning does.
EDIT: just a slight update....When assigning an object on the right to a variable on the left (e.g., str = Object.new), the variable receives an object reference. When assigning one variable to another, the left-hand side variable receives a copy of the reference that the variable on the right contains. In either case, you are still storing object references in the left-hand side variable.

Your original didn't work because you kept modifying the same array instance a.
Take a dup of the original array each time before you modify it into a different array. Or, create a new instance of Array by not relying on a destructive method.
a = original_array
b = a.dup
... # do some modifications to `b`
perms << b
c = a.dup
... # do some modifications to `c`
perms << c
...

If you don't like reinventing the wheel, you can use the facets gem.
gem install facets
https://github.com/rubyworks/facets/blob/d96ec0d700d1d7180ccbb5452e0a926386ec0b32/lib/backport/facets/array/permutation.rb
require 'facets'
[1, 2, 3].permutation
#=> [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Related

Ruby Second Smallest Number and index

Trying to use Ruby to find the second smallest number and the index from an input. I have the logic working from a hard coded array but can't seem to get the input from a user to work successfully. Thoughts?
print "Enter a list of numbers: "
nums = [gets]
#nums = [3,1,7,5]
lists = nums.sort
A = lists[1]
B = nums.find_index(A)
print "The second smallest number is: #{A} and the index is #{B}"
Suppose the user entered
str = " 2, -3 , 4, 1\n"
Then
arr = str.split(/ *, */).map(&:to_i)
#=> [2, -3, 4, 1]
The regular expression matches zero or more spaces followed by a comma followed by zero or more spaces.
The second smallest element, together with its index, can be obtained as follows.
n, i = arr.each_with_index.min(2).last
#=> [1, 3]
n #=> 1
i #=> 3
See Enumerable#min.
The steps are as follows.
enum = arr.each_with_index
#=> #<Enumerator: [2, -3, 4, 1]:each_with_index>
We can see the elements that will be generated by this enumerator by converting it to an array.
enum.to_a
#=> [[2, 0], [-3, 1], [4, 2], [1, 3]]
Continuing,
a = enum.min(2)
#=> [[-3, 1], [1, 3]]
min(2) returns the two smallest elements generated by enum. min compares each pair of elements with the method Array#<=>. (See especially the third paragraph of the doc.) For example,
[2, 0] <=> [-3, 1]
#=> 1
[4, 2] <=> [1, 3]
#=> 1
[1, 3] <=> [1, 4]
#=> -1
min would therefore order these pairs as follows.
[2, 0] > [-3, 1]
[4, 2] > [1, 3]
[1, 3] < [1, 4]
I've included the last example (though enum.to_a does not contain a second 1 at index 4) to illustrate that the second element of each two-element array serves as a tie-breaker.
As we want the second-smallest element of arr there is one final step.
n, i = a.last
#=> [1, 3]
n #=> 1
i #=> 3
Note that
n, i = [3, 2, 4, 2].each_with_index.min(2).last
#=> [2, 3]
n #=> 2
If we wanted n to equal 3 in this case we could write
n, i = [3, 2, 4, 2].uniq.each_with_index.min(2).last
#=> [3, 0]
n #=> 3
If the entries were floats or a mix of floats and integers we need only replace to_i with to_f.
str = "6.3, -1, 2.4, 3\n"
arr = str.split(/ *, */).map(&:to_f)
#=> [6.3, -1.0, 2.4, 3.0]
n, i = arr.each_with_index.min(2).last
#=> [2.4, 2]
n #=> 2.4
i #=> 2
Scan Input Strings and Map to Integers
The Kernel#gets method returns a String, so you have to parse and sanitize the user input to convert it to an Array. For example:
nums = gets.scan(/\d+/).map &:to_i
This uses String#scan to parse the input string, and Array#map to feed each element of the resulting array to String#to_i. The return value of this method chain will be an Array, which is then assigned to your nums variable.
Results of Example Data
Given input with inconsistent spacing or numbers of digits like:
1,2, 3, 4, 5, 10, 201
the method chain will nevertheless assign sensible values to nums. For example, the input above yields:
#=> [1, 2, 3, 4, 5, 10, 201]

Convert Ruby array of elements to Hash of counts with indices

Given a two dimensional array in Ruby:
[ [1, 1, 1],
[1, 1],
[1, 1, 1, 1],
[1, 1]
]
I'd like to create a Hash, where the keys are the counts of each internal array, and the values are arrays of indices of the original array whose internal array sizes have the particular count. The resulting Hash would be:
{ 2 => [1, 3], 3 => [0], 4 => [2] }
How do I concisely express this functionally in Ruby? I am attempting something akin to Hash.new([]).tap { |h| array.each_with_index { |a, i| h[a.length] << i } }, but the resulting Hash is empty.
There are two problems with your code. The first is that when h is empty and you write, say, h[2] << 1, since h does not have a key 2, h[2] returns the default, so this expression becomes [] << 1 #=> [1], but [1] is not attached to the hash, so no key and value are added.
You need to write h[2] = h[2] << 11. If you do that, your code returns h #=> {3=>[0, 1, 2, 3], 2=>[0, 1, 2, 3], 4=>[0, 1, 2, 3]}. Unfortunately, that's still incorrect, which takes us to the second problem with your code: you did not define the newly-created hash's default value correctly.
First note that
h[3].object_id
#=> 70113420279440
h[2].object_id
#=> 70113420279440
h[4].object_id
#=> 70113420279440
Aha, all three values are the same object! new's argument [] is returned by h[k] when h does not have a key k. The problem is that is the same array is returned for all keys k added to the hash, so you would be adding a key-value pair to an empty array for the first new key, then adding a second key-value pair to that same array for the next new key, and so on. See below for how the hash needs to be defined.
With these two changes your code works fine, but I would suggest writing it as follows.
arr = [ [1, 1, 1], [1, 1], [1, 1, 1, 1], [1, 1] ]
arr.each_with_index.with_object(Hash.new {|h,k| h[k]=[]}) { |(a,i),h|
h[a.size] << i }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
which use the form of Hash::new that uses a block to calculate the hash's default value (i.e., the value returned by h[k] when a hash h does not have a key k),
or
arr.each_with_index.with_object({}) { |(a,i),h| (h[a.size] ||= []) << i }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
both of which are effectively the following:
h = {}
arr.each_with_index do |a,i|
sz = a.size
h[sz] = [] unless h.key?(sz)
h[a.size] << i
end
h #=> {3=>[0], 2=>[1, 3], 4=>[2]}
Another way is to use Enumerable#group_by, grouping on array size, after picking up the index for each inner array.
h = arr.each_with_index.group_by { |a,i| a.size }
#=> {3=>[[[1, 1, 1], 0]],
# 2=>[[[1, 1], 1], [[1, 1], 3]],
# 4=>[[[1, 1, 1, 1], 2]]}
h.each_key { |k| h[k] = h[k].map(&:last) }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
1 The expression h[2] = h[2] << 1 uses the methods Hash#[]= and Hash#[], which is why h[2] on the left of = does not return the default value. This expression can alternatively be written h[2] ||= [] << 1.
arry = [ [1, 1, 1],
[1, 1],
[1, 1, 1, 1],
[1, 1]
]
h = {}
arry.each_with_index do |el,i|
c = el.count
h.has_key?(c) ? h[c] << i : h[c] = [i]
end
p h
This will give you
{3=>[0], 2=>[1, 3], 4=>[2]}

Change value of a cloned object

I have an object that has an instance variable array called my_array. I declare it via attr_accessor.
my_object.my_array = [1,2,3] # <= I don't know the max size of my_array(it can be dynamic)
I want to create the same object with my_object and fill its my_array with only one element. The value inside that element is each value from my_array's element (from my_object). Because the size of my_array is dynamic I suppose I need to iterate it via each.
Here's my attempt:
my_object.my_array.each do |element| # <= my_object is a special object
new_object = nil unless new_object.nil?
new_object = my_object.clone # <= I create new object with same class with my_object
new_object.my_array.clear # <= clear all element inside it.
new_object.my_array.push(element) # assign element value to the first element.
# rest of code #
new_object = nil
end
The iteration is not iterating properly. The size of my_object.my_array is 3, then it should iterating three times, but it's not, it's only iterating one time. I believe it's because of new_object.my_array.clear, but I cloned it from my_object, so why this happened?
When you assign one array it just copies the reference and both of them point to the same reference,
so a change in one is reflected when you print either of them:
orig_array = [1,2,3,4]<br>
another_array = orig_array
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
To avoid this you can use Marshal to copy from the original array without impacting the object to which it is copied.
Any changes in the original array will not change the object to which it is copied.
orig_array = [1,2,3,4]
another_array = Marshal.load(Marshal.dump(orig_array))
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[1, 2, 3, 4]
The problem is, that clone will make a shallow clone, not a deep clone. In other words, my_array is a reference and the cloned instance references the same array in memory. Consider:
class MyClass
attr_accessor :my_array
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # also changed!
#=> [1, 2, 3, 4]
In order to fix this, you need to extend the initialize_copy method to also clone the array:
class MyClass
attr_accessor :my_array
def initialize_copy(orig)
super
self.my_array = orig.my_array.clone
end
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # did not change, works as expected
#=> [1, 2, 3]

How to take one element out of an array and put in front?

a = [1,2,3]
b = [2,1,3]
What is the best way to get b from a?
My inelegant solution:
x = 2
y = a - [x]
b = y.unshift(x)
a.unshift a.delete(2)
This appends the recently deleted object (here 2).
Beware that, if the object in question appears more than once in the array, all occurences will be deleted.
In case you want only the first occurrence of an object to be moved, try this:
a = [1,2,3,2]
a.unshift a.delete_at(a.index(2))
# => [2, 1, 3, 2]
a.unshift a.slice!(a.index(2)||0)
# => [2, 1, 3]
If there are multiple instances, only the first instance is moved to the front.
If the element doesn't exist, then a is unchanged.
If you wanted to move elements of an array arbitrarily, you could do something like this:
Code
# Return a copy of the receiver array, with the receiver's element at
# offset i moved before the element at offset j, unless j == self.size,
# in which case the element at offset i is moved to the end of the array.
class Array
def move(i,j)
a = dup
case
when i < 0 || i >= size
raise ArgumentError, "From index is out-of-range"
when j < 0 || j > size
raise ArgumentError, "To index is out-of-range"
when j < i
a.insert(j, a.delete_at(i))
when j == size
a << a.delete_at(i)
when j > i+1
a.insert(j-1, a.delete_at(i))
else
a
end
end
end
With Ruby v2.1, you could optionally replace class Array with refine Array. (Module#refine was introduced experimentally in v2.0, but was changed substantially in v2.1.)
Demo
arr = [1,2,3,4,5] #=> [1, 2, 3, 4, 5]
arr.move(2,1) #=> [1, 3, 2, 4, 5]
arr.move(2,2) #=> [1, 2, 3, 4, 5]
arr.move(2,3) #=> [1, 2, 3, 4, 5]
arr.move(2,4) #=> [1, 2, 4, 3, 5]
arr.move(2,5) #=> [1, 2, 4, 5, 3]
arr.move(2,6) #=> ArgumentError: To index is out-of-range

How to drop the end of an array in Ruby

Array#drop removes the first n elements of an array. What is a good way to remove the last m elements of an array? Alternately, what is a good way to keep the middle elements of an array (greater than n, less than m)?
This is exactly what Array#pop is for:
x = [1,2,3]
x.pop(2) # => [2,3]
x # => [1]
You can also use Array#slice method, e.g.:
[1,2,3,4,5,6].slice(1..4) # => [2, 3, 4, 5]
or
a = [1,2,3,4,5,6]
a.take 3 # => [1, 2, 3]
a.first 3 # => [1, 2, 3]
a.first a.size - 1 # to get rid of the last one
The most direct opposite of drop (drop the first n elements) would be take, which keeps the first n elements (there's also take_while which is analogous to drop_while).
Slice allows you to return a subset of the array either by specifying a range or an offset and a length. Array#[] behaves the same when passed a range as an argument or when passed 2 numbers
this will get rid of last n elements:
a = [1,2,3,4,5,6]
n = 4
p a[0, (a.size-n)]
#=> [1, 2]
n = 2
p a[0, (a.size-n)]
#=> [1, 2, 3, 4]
regard "middle" elements:
min, max = 2, 5
p a.select {|v| (min..max).include? v }
#=> [2, 3, 4, 5]
I wanted the return value to be the array without the dropped elements. I found a couple solutions here to be okay:
count = 2
[1, 2, 3, 4, 5].slice 0..-(count + 1) # => [1, 2, 3]
[1, 2, 3, 4, 5].tap { |a| a.pop count } # => [1, 2, 3]
But I found another solution to be more readable if the order of the array isn't important (in my case I was deleting files):
count = 2
[1, 2, 3, 4, 5].reverse.drop count # => [3, 2, 1]
You could tack another .reverse on there if you need to preserve order but I think I prefer the tap solution at that point.
You can achieve the same as Array#pop in a non destructive way, and without needing to know the lenght of the array:
a = [1, 2, 3, 4, 5, 6]
b = a[0..-2]
# => [1, 2, 3, 4, 5]
n = 3 # if we want drop the last n elements
c = a[0..-(n+1)]
# => [1, 2, 3]
Array#delete_at() is the simplest way to delete the last element of an array, as so
arr = [1,2,3,4,5,6]
arr.delete_at(-1)
p arr # => [1,2,3,4,5]
For deleting a segment, or segments, of an array use methods in the other answers.
You can also add some methods
class Array
# Using slice
def cut(n)
slice(0..-n-1)
end
# Using pop
def cut2(n)
dup.tap{|x| x.pop(n)}
end
# Using take
def cut3(n)
length - n >=0 ? take(length - n) : []
end
end
[1,2,3,4,5].cut(2)
=> [1, 2, 3]

Resources