Получить массив массивов с уникальными элементами

У меня есть такой массив:

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

Я хочу знать, есть ли способ для этого:

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

Я знаю, что есть Array.uniq, но это удаляет повторяющиеся элементы, и я хотел бы сохранить их.

Ответ 1

Не уверен в производительности, но это работает:

код:

$ cat foo.rb
require 'pp'

array = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
result = []

values = array.group_by{|e| e}.values

while !values.empty?
  result << values.map{|e| e.slice!(0,1)}.flatten
  values = values.reject!{|e| e.empty?}
end

pp result

Выход:

$ ruby foo.rb
[[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

Ответ 2

Вот несколько способов сделать это.

arr = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]

# 1

b = []
a = arr.dup
while a.any?
  u = a.uniq
  b << u
  a = a.difference u
end
b
  #=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]] 

Помощник Array#difference определяется в моем ответе здесь.

# 2

arr.map { |n| [n, arr.count(n)] }
   .each_with_object({}) { |(n,cnt),h|
     (1..cnt).each { |i| (h[i] ||= []) << n } }
   .values
   .map(&:uniq)
  #=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

Этапы для:

arr = [1, 2, 3, 3, 6, 6, 6, 7]

a = arr.map { |n| [n, arr.count(n)] }
  #=> [[1, 1], [2, 1], [3, 2], [3, 2], [4, 2], [4, 2],
  #    [5, 1], [6, 3], [6, 3], [6, 3], [7, 1]] 
enum = a.each_with_object({})
  #=> #<Enumerator: [[1, 1], [2, 1], [3, 2], [3, 2], [4, 2], [4, 2],
  #   [5, 1], [6, 3], [6, 3], [6, 3], [7, 1]]:each_with_object({})> 

Чтобы просмотреть элементы enum:

enum.to_a
  #=> [[[1, 1], {}], [[2, 1], {}],...[[7, 1], {}]] 

Теперь перейдите к перечислителю и просмотрите хэш после каждого шага:

(n,cnt),h = enum.next
    #=> [[1, 1], {}] 
n   #=> 1 
cnt #=> 1 
h   #=> {} 
(1..cnt).each { |i| (h[i] ||= []) << n }
h   #=> {1=>[1]} 

(n,cnt),h = enum.next
  #=> [[2, 1], {1=>[1]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2]} 

(n,cnt),h = enum.next
  #=> [[3, 2], {1=>[1, 2]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3], 2=>[3]} 

(n,cnt),h = enum.next
  #=> [[3, 2], {1=>[1, 2, 3], 2=>[3]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3], 2=>[3, 3]} 

(n,cnt),h = enum.next
  #=> [[6, 3], {1=>[1, 2, 3, 3], 2=>[3, 3]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6], 2=>[3, 3, 6], 3=>[6]} 

(n,cnt),h = enum.next
  #=> [[6, 3], {1=>[1, 2, 3, 3, 6], 2=>[3, 3, 6], 3=>[6]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6], 2=>[3, 3, 6, 6], 3=>[6, 6]} 

(n,cnt),h = enum.next
  #=> [[6, 3], {1=>[1, 2, 3, 3, 6, 6], 2=>[3, 3, 6, 6], 3=>[6, 6]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6, 6], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]} 

(n,cnt),h = enum.next
  #=> [[7, 1], {1=>[1, 2, 3, 3, 6, 6, 6], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]}] 
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6, 6, 7], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]} 

Наконец, извлеките и унифицируйте значения хэша:

b = h.values
  #=> [[1, 2, 3, 3, 6, 6, 6, 7], [3, 3, 6, 6, 6], [6, 6, 6]]
b.map(&:uniq)
  #=> [[1, 2, 3, 6, 7], [3, 6], [6]]

Ответ 3

В рубине вы можете добавить метод к классу Array. Вот так:

class Array
    def uniqA (acc)
        return acc if self.empty?
        # return self.replace acc if self.empty? #to change the object itself
        acc << self.uniq

        self.uniq.each { |x| self.delete_at(self.index(x)) }

        uniqA(acc)
    end
end

b = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]

print b.uniqA([])
 #=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

print b
 #=> []

Или вы могли бы сделать это, чтобы сохранить элементы на b:

b = b.uniqA([])
 #=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

print b
 #=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

Ответ 4

Простое решение, но я уверен, что он не будет иметь лучшую производительность:

def array_groups(arr)
  result = []
  arr.uniq.each do |elem|
    arr.count(elem).times do |n|
      result[n] ||= []
      result[n] << elem
    end
  end
  result
end

array_groups [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
# [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]

Ответ 5

[1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
.each.with_object([]){|e, a| (a.find{|b| !b.include?(e)} || a.push([]).last).push(e)}
# => [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]