Сортировать массив на основе класса другого элемента массива - PullRequest
0 голосов
/ 28 сентября 2018

Я ищу способ сортировки элементов в массиве на основе классов элементов в другом массиве.У меня есть:

order = [String, Integer, NilClass ,TrueClass]
arry = [1, 2, 3, 4, 5, true, false, nil, 34, nil, 'Hello', 'World']

В order порядок элементов является динамическим.Результат должен быть:

result_arry = ['Hello', 'World', 1, 2, 3, 4, 5, 34, nil, nil, true, false]

Как мне отсортировать arry на основе класса элементов в order?

Я пытался:

hash = arry.group_by  {|n| n.class } 
# => {Fixnum=>[1, 2, 3, 4, 5, 34], TrueClass=>[true], FalseClass=>[false], NilClass=>[nil, nil], String=>["Hello", "World"]}

Ответы [ 3 ]

0 голосов
/ 28 сентября 2018

Использование group_by - хороший подход, поскольку он сохраняет порядок элемента:

hash = arry.group_by(&:class)
#=> {
#     Integer => [1, 2, 3, 4, 5, 34],
#     TrueClass => [true],
#     FalseClass => [false],
#     NilClass => [nil, nil],
#     String => ["Hello", "World"]
#   }

. Мы можем использовать sort_by для сортировки хеша по ключам на основе каждогоключи index в массиве order.Если класс отсутствует в orders, мы используем массив size в качестве запасного варианта, чтобы отсортировать его последним:

sorted = hash.sort_by { |k, _| order.index(k) || order.size }
#=> [
#     [String, ["Hello", "World"]],
#     [Integer, [1, 2, 3, 4, 5, 34]],
#     [NilClass, [nil, nil]],
#     [TrueClass, [true]],
#     [FalseClass, [false]]
#  ]

Наконец, flat_map извлекает last часть каждого элемента:

sorted.flat_map(&:last)
#=> ["Hello", "World", 1, 2, 3, 4, 5, 34, nil, nil, true, false]

Вы также можете использовать sort_by без предварительной группировки, но это может перемешать элементы с тем же классом:

arry.sort_by { |e| order.index(e.class) || order.size }
#=> ["World", "Hello", 3, 4, 5, 1, 2, 34, nil, nil, true, false]

Это потому, что sort_by не стабильно.

Чтобы исправить это, мы должны принять во внимание их индексы:

arry.sort_by.with_index { |e, i| [order.index(e.class) || order.size, i] }
#=> ["Hello", "World", 1, 2, 3, 4, 5, 34, nil, nil, true, false]

Если вы также хотите сопоставить подклассы (например, make Integer match Fixnum в более старых версиях Ruby), вы должны передать блок index:

order.index { |cls| e.is_a?(cls) }
0 голосов
/ 27 октября 2018

Сортировка, сама по себе , не обязательна.

missing_classes = arry.map(&:class) - order
  #=> [Symbol, FalseClass]
arry.group_by(&:class).values_at(*(order + missing_classes)).flatten
  #=> ["Hello", "World", 1, 2, 3, 4, 5, 34, nil, nil, true, :cat, false]
0 голосов
/ 28 сентября 2018

Я думаю, что это работает:

arry.sort_by{ |v|
    order.map{ |c|
        v.is_a?(Object.const_get(c)) ? -1 : 1
    }
}

# ["Hello", "World", 1, 2, 3, 4, 5, 34, nil, nil, true, false]

Object.const_get(c) преобразует строку в класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...