ruby set isn't unique - ruby

For some reason the following code produce a set with duplicate values.
I'm not sure how uniqueness of an array in ruby is defined so maybe this is somehow expectable?
require 'set'
xs = [1, 2, 3]
xss = Set.new []
xs.each do |x|
xss.merge xss.to_a.map{|xs| xs.push x}
xss.add [x]
p xss
end
will prints
#<Set: {[1]}>
#<Set: {[1, 2], [1, 2], [2]}>
#<Set: {[1, 2, 3, 3], [1, 2, 3, 3], [2, 3], [1, 2, 3, 3], [2, 3], [3]}>
What's wrong?
EDIT
change xs.push x to xs + [x] will fix it.

You are effectively altering the objects within the set, which is not allowed.
From the documentation:
Set assumes that the identity of each element does not change while it is stored. Modifying an element of a set will render the set to an unreliable state.
Regarding your comment
I want #<Set: {[1], [1, 2], [2], [1, 3], [1, 2, 3], [2, 3], [3]}>
You could use Array#combination:
a = [1, 2, 3]
(1..a.size).flat_map { |n| a.combination(n).to_a }
#=> [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]

Related

Combine all combinations to get complete sets

I have an array:
arr = [1, 2, 3]
I want to find all combinations and then combine the combinations to get arrays that contain all the elements of arr only once. The sequence doesn't matter. The first combination should return something like
combis = [
[1], [2], [3],
[1, 2], [1, 3], [2, 3],
[1, 2, 3]
]
I need valid that has the combinations of combis that contain each value from arr exactly once. So:
valid = [
[[1], [2], [3]],
[[1], [2, 3]],
[[2], [1, 3]],
[[3], [1, 2]],
[[1, 2, 3]]
]
This gets large very quickly, so I need a way to do this without using the combination function twice and then filtering out the incorrect ones.
I feel I need to use some kind of tree structure and recursion to generate the second set of combinations and stop traversing when it is no longer a valid final set.
Would be great if someone could help me with the (pseudo)code for this.
Use Enumerator::Lazy to reject unwanted / invalid combinations immediately:
combis = 1.upto(arr.size).each_with_object([]) do |i, acc|
acc.concat arr.combination(i).to_a
end
#⇒ [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
valid = 1.upto(arr.size).each_with_object([]) do |i, acc|
acc.concat(
# ⇓⇓⇓⇓ THIS
combis.combination(i).lazy.select do |e|
items = e.flatten
items.uniq.size == items.size && items | arr == items
end.to_a
)
end
#⇒ [[[1, 2, 3]], [[1], [2, 3]], [[2], [1, 3]], [[3], [1, 2]], [[1], [2], [3]]]

How to make an array containing a duplicate of an array

I couldn't find a way to build an array such as
[ [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] ]
given [1,2,3] and the number 5. I guess there are some kind of operators on arrays, such as product of mult, but none in the doc does it. Please tell me. I missed something very simple.
Array.new(5, [1, 2, 3]) or Array.new(5) { [1, 2, 3] }
Array.new(size, default_object) creates an array with an initial size, filled with the default object you specify. Keep in mind that if you mutate any of the nested arrays, you'll mutate all of them, since each element is a reference to the same object.
array = Array.new(5, [1, 2, 3])
array.first << 4
array # => [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
Array.new(size) { default_object } lets you create an array with separate objects.
array = Array.new(5) { [1, 2, 3] }
array.first << 4
array #=> [[1, 2, 3, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
Look up at the very top of the page you linked to, under the section entitled "Creating Arrays" for some more ways to create arrays.
Why not just use:
[[1, 2, 3]] * 5
# => [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
The documentation for Array.* says:
...returns a new array built by concatenating the int copies of self.
Typically we'd want to create a repeating single-level array:
[1, 2, 3] * 2
# => [1, 2, 3, 1, 2, 3]
but there's nothing to say we can't use a sub-array like I did above.
It looks like mutating one of the subarrays mutates all of them, but that may be what someone wants.
It's like Array.new(5, [1,2,3]):
foo = [[1, 2, 3]] * 2
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
foo = Array.new(2, [1,2,3])
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
A work-around, if that's not the behavior wanted is:
foo = ([[1, 2, 3]] * 2).map { |a| [*a] }
foo[0][0] = 4
foo # => [[4, 2, 3], [1, 2, 3]]
But, at that point it's not as convenient, so I'd use the default Array.new(n) {…} behavior.

ruby array element grouping

here is the problem
I have an array:
a = [1, 2, 3, 4]
and want to get:
b = [[1, 2, 3], [1, 3, 4], [1, 2, 4], [2, 3, 4]]
what is the best way? thanks!
You are looking for all unique sets of 3 elements out of a set of 4.
Use Array#combination method:
a = [1, 2, 3, 4]
b = a.combination(3).to_a
output:
=> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
More info:
Array#combination
Wikipedia Combination
Here would be my first implementation. (But performance suckz i guess)
array = [1,2,­3,4]
b = []
array.each­{|e| c = array­.clone; c.del­ete(e); b << c}
# b.sort!

Creating permutations from a multi-dimensional array in Ruby

I have the following multi-dimensional array in Ruby:
[[1,2], [3], [4,5,6]]
I need to have the following output:
[[1,3,4], [1,3,5], [1,3,6], [2,3,4], [2,3,5], [2,3,6]]
I have tried creating a recursive function, but I'm not having much luck.
Are there any Ruby functions that would help with this? Or is the only option to do it recursively?
Thanks
Yup, Array#product does just that (Cartesian product):
a = [[1,2], [3], [4,5,6]]
head, *rest = a # head = [1,2], rest = [[3], [4,5,6]]
head.product(*rest)
#=> [[1, 3, 4], [1, 3, 5], [1, 3, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6]]
Another variant:
a.inject(&:product).map(&:flatten)
#=> [[1, 3, 4], [1, 3, 5], [1, 3, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6]]

Sorting an array by two values

Suppose I have
an_array = [[2, 3], [1, 4], [1, 3], [2, 1], [1, 2]]
I want to sort this array by the first value of each inner array, and then by the second (so the sorted array should look like this: [[1, 2], [1, 3], [1, 4], [2, 1], [2, 3]])
What's the most readable way to do this?
This is the default behavior for sorting arrays (see the Array#<=> method definition for proof). You should just be able to do:
an_array.sort
If you want some non-default behaviour, investigate sort_by (ruby 1.8.7+)
e.g. sort by the second element then by the first
a.sort_by {|e| [e[1], e[0]]} # => [[2, 1], [1, 2], [1, 3], [2, 3], [1, 4]]
or sort by the first element ascending and then the second element descending
a.sort_by {|e| [e[0], -e[1]]} # => [[1, 4], [1, 3], [1, 2], [2, 3], [2, 1]]
an_array.sort

Resources