How to More Elegantly Remove Duplicate Items Across All Elements of a Ruby Array

Remove duplicates from array in Ruby and perform an operation on a specific index

arr.each_with_object(Hash.new(0)) { |(*a,n),h| h[a] += n }.map(&:flatten)
#=> [["A", "Red", 15], ["B", "Red", 3], ["B", "Blue", 5], ["C", "Blue", 3],
# ["C", "Black", 1], ["D", nil, 9]]

The first step of the calculation is:

h = arr.each_with_object(Hash.new(0)) { |(*a,n),h| h[a] += n }
#=> {["A", "Red"]=>15, ["B", "Red"]=>3, ["B", "Blue"]=>5,
# ["C", "Blue"]=>3, ["C", "Black"]=>1, ["D", nil]=>9}

This uses the form of Hash::new that takes an argument called the default value. All that means is that when Ruby's parser expands h[a] += 1 to

h[a] = h[a] + n

h[a] on the right returns h's default value, 0, if h does not have a key a. For example, when h is empty,

h[["A", "Red"]] = h[["A", "Red"]] + 7 #=> 0 + 7 =>  7
h[["A", "Red"]] = h[["A", "Red"]] + 8 #=> 7 + 8 => 15

h does not have a key ["A", "Red"] in the first expression, so h[["A", "Red"]] on the right returns the default value, 0, whereas h does have that key in the second expression so the default value does not apply.

h.map(&:flatten) is shorthand for

h.map { |a| a.flatten }

When the block variable a is set equal to first key-value pair of h,

a #=> [["A", "Red"], 15]

So

a.flatten
#=> ["A", "Red", 15]

To understand|(*a,n),h| we need to construct the enumerator

enum = arr.each_with_object(Hash.new(0))
#=> #<Enumerator: [["A", "Red", 7], ["A", "Red", 8], ["B", "Red", 3],
# ["B", "Blue", 2], ["B", "Blue", 3], ["C", "Blue", 3],
# ["C", "Black", 1], ["D", nil, 4], ["D", nil, 5]]
# :each_with_object({})>

We now generate the first value from the enumerator (using Enumerator#next) and assign values to the block variables:

(*a,n),h = enum.next
#=> [["A", "Red", 7], {}]
a #=> ["A", "Red"]
n # => 7
h #=> {}

The way in which the array returned by enum.next is broken up into constituent elements that are assigned to the block variables is called array decomposition. It is a powerful and highly useful techique.

Ruby - Merge two arrays and remove values that have duplicate

You can do the following!

# Merging
c = a + b
=> [1, 2, 3, 4, 5, 2, 4, 6]
# Removing the value of other array
# (a & b) is getting the common element from these two arrays
c - (a & b)
=> [1, 3, 5, 6]

Dmitri's comment is also same though I came up with my idea independently.

How can I remove duplicates in an array without using `uniq`?

the problem is that the inner loop is an infinite loop:

while true
sorted.delete_if {|i| i = i + count}
count += 1
end #while

you can probably do what you are doing but it's not eliminating duplicates.

one way to do this would be:

numbers = [1, 4, 2, 4, 3, 1, 5]
target = []
numbers.each {|x| target << x unless target.include?(x) }
puts target.inspect

to add it to the array class:

class ::Array
def my_uniq
target = []
self.each {|x| target << x unless target.include?(x) }
target
end
end

now you can do:

numbers = [1, 4, 2, 4, 3, 1, 5]
numbers.my_uniq

Ruby removing duplicates in enumerable lists

For array you can use uniq() method

a = [ "a", "a", "b", "b", "c" ]
a.uniq #=> ["a", "b", "c"]

so if you just

(1..10).to_a.uniq

or

%w{ant bat cat ant}.to_a.uniq

because anyway almost every methods you do implement will return as an Array class.

How to remove duplicate contiguous elements in an array?

I know you can do it with chunk_while and map, but there might be other ways:

[1,1,1,2,2,3,4,1,2].chunk_while { |e, f| e == f }.map(&:first)
# [1, 2, 3, 4, 1, 2]

With chunk_while you split the array by chunks whenever the block is evaluated to true, for that the block yields two variables, the "element before" and the "element after", that's to say, for every iteration you're going to get this:

[1, 1]
[1, 1]
[1, 2]
[2, 2]
[2, 3]
[3, 4]
[4, 1]
[1, 2]

After applying the logic in the proc, it'll chunk the receiver whenever the first and second yielded elements are equal, and you get:

[[1, 1, 1], [2, 2], [3], [4], [1], [2]]

After that you can map that result to get only one element from each array - there are many ways, but first is enough.

The chunk_while proc can also be shortened to (&:==), leaving as [1,1,1,2,2,3,4,1,2].chunk_while(&:==).map(&:first).

Similarly and just out of curiosity, you can use slice_when and last to save 2 characters: [1,1,1,2,2,3,4,1,2].slice_when(&:!=).map(&:last).

Compare arrays and remove duplicates, in Ruby?

It's just set difference or subtraction and you can write it as such. Operator overloading can be a bliss :)

a is what it is.

a
[[2, 1], [3, 3], [7, 2], [5, 6]]

b = b - a
[[6, 7], [9, 9], [4, 3]]

c = c - b - a # or c - (a + b)
[[1, 1], [2, 2]]

d = d - c - b - a # or d - (a + b + c)
[[3, 1]]

2D array - How to remove duplicate values but keep the sub arrays separated

The most generic approach would be:

[[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]].
each_with_object([]) { |a, acc| acc << a - acc.flatten }
#⇒ [[1, 2, 3, 4], [5], [6], [7]]

or

[[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]].
reduce([]) { |acc, a| acc << a - acc.flatten }
#⇒ [[1, 2, 3, 4], [5], [6], [7]]


Related Topics



Leave a reply



Submit