delete similar elements in an array in ruby - ruby

I have the following array
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
I want to get the following output [["a","b"], ["a","c"], ["b","c"]] and delete ["b","a"] and ["c","b"]
I have the following code
a3.each do |ary3|
x = ary3[0]
y = ary3[1]
x = ary3[1]
y = ary3[0]
if a3.include?([x,y])
a3 - [y,x]
end
end
print a3
I tried using the swap, but no luck!
Thanks for the help.

Two arrays are considered to be equal if they contain the same elements and these elements are in the same order:
["a", "b"] == ["b", "a"]
#=> false
["a", "b"] == ["a", "b"]
#=> true
So you need to sort the inner arrays first and then you can use Array#uniq to ensure that each element in the outer array will only appear once:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map(&:sort).uniq
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
This will leave arr untouched, however:
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
You will need to use mutator methods (with a !) to edit the array in place:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map!(&:sort).uniq!
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
Edit
As a follow-up to #sawa's comment, who was concerned that it may not be desirable to change the ordering of the inner arrays, i looked a bit deeper into Array#uniq. Consider the following array:
arr = [["b", "a"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
I figured out that Array#uniq actually takes a block that lets you specify how the elements should be be compared:
arr.uniq!{|x| x.sort }
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
Cool thing is, this also works with Symbol#to_proc (the &: notation) and actually looks even more elegant than my original answer:
arr.uniq!(&:sort)
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
You can still use Array#sort! if you want the inner arrays to be sorted afterwards:
arr.uniq!(&:sort!)
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
My last observation on this is though, that the order probably isn't important or else two arrays with different order would not be considered equal. This got me thinking (again) and i posed myself the question: Why not use a Set? It would work like this:
require 'set'
sets = [Set["a", "b"], Set["a", "c"], Set["b", "c"], Set["b", "a"], Set["c", "b"]]
sets.uniq!
sets
#=> [#<Set: {"a", "b"}>, #<Set: {"a", "c"}>, #<Set: {"b", "c"}>]
Just keep in mind that a Set will not allow you to add the same element multiple times, whereas an array does:
[%w[a b b b c], %w[a b b b c], %w[a b c]].uniq(&:sort)
#=> [["a", "b", "b", "b", "c"], ["a", "b", "c"]]
[Set.new(%w[a b b b c]), Set.new(%w[a b b b c]), Set.new(%w[a b c])].uniq
#=> [#<Set: {"a", "b", "c"}>]

check this one: #delete_if as below:
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
p a3.delete_if{|x| [["b", "a"], ["c","b"]].include? x}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
As per your comment and description post :
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
p a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
BenchMark:
require 'benchmark'
N = 10000
Benchmark.bm(20) do | x |
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('Mine') do
N.times { a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse} }
end
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('padde') do
N.times { a3.uniq!(&:sort!) }
end
end
Output:
user system total real
Mine 0.172000 0.000000 0.172000 ( 0.361021)
padde 0.203000 0.000000 0.203000 ( 0.460026)

Related

How can i change the output order of a multi-dimensional array in Ruby? [duplicate]

This question already has answers here:
How to create all possible combinations with two arrays
(4 answers)
Closed 3 years ago.
I have these two arrays:
m1 = ["a", "b", "c"]
m2 = ["yes", "no"]
and I expect the following result:
expected-output = [["a", "yes"], ["a", "no"],
["b", "yes"], ["b", "no"],
["c", "yes"], ["c", "no"]]
This is the code that I tried:
array1 = []
array2 = []
m2.map { |e| m1.map {|i| array1 << i and array2 << e }}
newArray = array1.zip(array2)
print newArray
but my outcome is not in the right order:
[["a", "yes"], ["b", "yes"],
["c", "yes"], ["a", "no"],
["b", "no"], ["c", "no"]]
PS: I already used the sort method and it didn't work.
for this you must use the Product function available on Ruby.
https://apidock.com/ruby/Array/product
> m1 = ["a", "b", "c"]
=> ["a", "b", "c"]
> m2 = ["yes", "no"]
=> ["yes", "no"]
> m1.product(m2)
=> [["a", "yes"], ["a", "no"], ["b", "yes"], ["b", "no"], ["c", "yes"], ["c", "no"]]

How to make all possible pairs from an array

a = ["a","b","c"]
a.each_cons(2).to_a # => [["a", "b"], ["b", "c"]]
I want three possible pairs from this array
You can use Array#permutation to generate sub-arrays with all combination.
a.permutation(2).to_a
# => [["a", "b"], ["a", "c"], ["b", "a"], ["b", "c"], ["c", "a"], ["c", "b"]]
Following this you can pick 3 random arrays using Array#sample (assuming you want to pick random sub-arrays). Here:
a.permutation(2).to_a.sample(3)
# => [["c", "b"], ["c", "a"], ["b", "c"]]
Try this:
a = ["a","b","c"]
a.permutation(2).to_a.take(3)
# => [["a", "b"], ["a", "c"], ["b", "a"]]
a = ["a","b","c"]
a.combination(2).to_a # => [["a", "b"], ["a", "c"], ["b", "c"]]

Partial cartesian product (ensure one value in each set)

Given a set of items [z,a,b,c] I want to find the "cartesian power" (cartesian product with itself n times) but only those results that have a z in them. For example:
normal_values = ["a","b","c"]
p limited_cartesian( normal_values, "z", 2 )
#=> [
#=> ["z", "z"]
#=> ["z", "a"]
#=> ["z", "b"]
#=> ["z", "c"]
#=> ["a", "z"]
#=> ["b", "z"]
#=> ["c", "z"]
#=> ]
I can do this by spinning through the full set and skipping entries that don't have the special value, but I'm wondering if there's a simpler way. Preferably one that allows me to only evaluate the desired entries lazily, without wasting time calculating unwanted entries.
def limited_cartesian( values, special, power )
[special, *values].repeated_permutation(power)
.select{ |prod| prod.include?( special ) }
end
Edit: With v3.0 I finally have something respectable. As is oft the case, the key was looking at the problem the right way. It occurred to me that I could repeatedly permute normal_values << special, power - 1 times, then for each of those permutations, there would be one more element to add. If the permutation contained at least one special, any element of normal_values << special could be added; otherwise, special must be added.
def limited_cartesian( values, special, power )
all_vals = values + [special]
all_vals.repeated_permutation(power-1).map do |p|
if p.include?(special)
*all_vals.each_with_object([]) { |v,a| a << (p + [v]) }
else
p + [special]
end
end
end
limited_cartesian( values, 'z', 1 )
# [["z"]]
limited_cartesian( values, 'z', 2 )
# => [["a", "z"], ["b", "z"], ["c", "z"],
# ["z", "a"], ["z", "b"], ["z", "c"],
# ["z", "z"]]
limited_cartesian( values, 'z', 3 )
# => [["a", "a", "z"], ["a", "b", "z"], ["a", "c", "z"],
# ["a", "z", "a"], ["a", "z", "b"], ["a", "z", "c"],
# ["a", "z", "z"], ["b", "a", "z"], ["b", "b", "z"],
# ["b", "c", "z"], ["b", "z", "a"], ["b", "z", "b"],
# ["b", "z", "c"], ["b", "z", "z"], ["c", "a", "z"],
# ["c", "b", "z"], ["c", "c", "z"], ["c", "z", "a"],
# ["c", "z", "b"], ["c", "z", "c"], ["c", "z", "z"],
# ["z", "a", "a"], ["z", "a", "b"], ["z", "a", "c"],
# ["z", "a", "z"], ["z", "b", "a"], ["z", "b", "b"],
# ["z", "b", "c"], ["z", "b", "z"], ["z", "c", "a"],
# ["z", "c", "b"], ["z", "c", "c"], ["z", "c", "z"],
# ["z", "z", "a"], ["z", "z", "b"], ["z", "z", "c"],
# ["z", "z", "z"]]
This is my v2.1, which works, but is not pretty. I'll leave it for the record.
def limited_cartesian( values, special, power )
ndx = Array(0...power)
ndx[1..-1].each_with_object( [[special]*power] ) do |i,a|
ndx.combination(i).to_a.product(values.repeated_permutation(power-i).to_a)
.each { |pos, val| a << stuff_special(special, pos, val.dup) }
end
end
def stuff_special( special, pos, vals )
pos.each_with_object(Array.new(pos.size + vals.size)) {|j,r|
r[j] = special }.map {|e| e.nil? ? vals.shift : e }
end
# e.g., stuff_special( 'z', [1,4], ["a","b","c"]) => ["a","z","b","c","z"]

Convert multiple level array to single level array?

I want to convert this array
[[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]]
into
[["b", "c"], ["c", "d"], ["v", "e"], ["r", "g"]]
How can I convert this ?
Array#flatten takes an optional level:
The optional level argument determines the level of recursion to flatten
Example:
[[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]].flatten(1)
#=> => [["b", "c"], ["c", "d"], ["v", "e"], ["r", "g"]]
arr = []
a = [[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]]
a.map{|x| x.map{|y| arr << y}}
puts arr

Merging arrays without losing original order, ruby

I have an array structure that looks like:
a=[
[['a','A'],['b','B'],['c','C']],
[['d','D'],['e','E'],['f','F']]
]
How to merge inner two arrays so the new structure will be Array of arrays
[
['a','A'],['b','B'],['c','C'],['d','D'],['e','E'],['f','F']
]
Tried
a.inject([]){|k,v| v | k} # but order gets changed
=> [["d", "D"], ["e", "E"], ["f", "F"], ["a", "A"], ["b", "B"], ["c", "C"]]
How can i get desired result without loosing the order.
Tips, comments, suggestions, please?
Thnx.
array.flatten takes a parameter:
a.flatten(1) #[["a", "A"], ["b", "B"], ["c", "C"], ["d", "D"], ["e", "E"], ["f", "F"]]
Try this:
a.inject([]){|k,v| k|v}

Resources