В ruby, как вы переставляете массив объектов с ключами id, задаете новый порядок массива как массива? - PullRequest
0 голосов
/ 17 мая 2019

У меня есть объект с массивом, который выглядит следующим образом:

some_object = {
  some_array: [
    { id: "foo0" },
    { id: "foo1" },
    { id: "foo2" },
    { id: "foo3" },
  ]
}

И у меня есть вход другого массива, который я хочу переставить массив в

target_order = [
  { id: "foo0", new_position: 3 },
  { id: "foo3", new_position: 0 },
  { id: "foo1", new_position: 2 },
  { id: "foo2", new_position: 1 }
]

Какмне использовать второй массив target_order для изменения порядка первого some_object[:some_array]?

Ответы [ 4 ]

1 голос
/ 17 мая 2019

Я рекомендую использовать sort_by с пользовательским блоком, который находит позицию элемента в новом массиве.

new_array = some_object[:some_array].sort_by do |item|
  order = target_order.detect { |order| order[:id] == item[:id] }
  next unless order

  order[:new_position]
end

Возвращает следующее значение.

=> [{:id=>"foo2"}, {:id=>"foo1"}, {:id=>"foo0"}, {:id=>"foo3"}]

Дополнительные соображения

Возможно, вы хотели назначить каждому элементу позицию в списке, а не просто сортировать их. Например

target_order = [
  { id: "foo0", new_position: 0 },
  { id: "foo1", new_position: 2 }
]

даст

=> [{ id: "foo0" }, nil, { id: "foo1" }]

Для этого следует использовать each_with_object вместо sort_by.

new_array = target_order.each_with_object([]) do |order, memo|
  item = some_object[:some_array].detect { |item| item[:id] == order[:id] }
  next unless item

  memo[order[:new_position]] = item
end
0 голосов
/ 17 мая 2019

Нет необходимости сортировать, которая имеет временную сложность O (n * log (n)).Вот решение O (n).

{ some_array: target_order.each_with_object([]) { |h,a|
    a[h[:new_position]] = h.slice(:id) } }
  #=> {:some_array=>[{:id=>"foo3"}, {:id=>"foo2"}, {:id=>"foo1"}, {:id=>"foo0"}]} 

Обратите внимание, что нет ссылки на some_object.

Если some_object должен быть изменен на месте:

some_object[:some_array] = target_order.each_with_object([]) { |h,a|
  a[h[:new_position]] = h.slice(:id) }
some_object
  #=> {:some_array=>[{:id=>"foo3"}, {:id=>"foo2"}, {:id=>"foo1"}, {:id=>"foo0"}]} 

Использование Enumerable # sort_by , хотя и менее эффективный, можно написать:

{ some_array: target_order.sort_by { |h| h[:new_position] }.map { |h| h.slice(:id) } }
  #=> {:some_array=>[{:id=>"foo3"}, {:id=>"foo2"}, {:id=>"foo1"}, {:id=>"foo0"}]} 
0 голосов
/ 17 мая 2019

Если между элементами some_array и target_order существует однозначное соответствие, возможно, вы можете выполнить прямое назначение, например:

some_object[:some_array] = target_order.sort_by{ |h| h[:new_position] }.map { |h| h.delete_if { |k, _| k == :new_position } }

Итак, вы получите

some_object #=> {:some_array=>[{:id=>"foo3"}, {:id=>"foo2"}, {:id=>"foo1"}, {:id=>"foo0"}]}
0 голосов
/ 17 мая 2019

Просто, чтобы быть упрощенным, это то, что я бы сделал ...

temp_arr = []

target_order.each do |o|
  x = some_json_object[:some_array].find { |i| o[:id] == i[:id] }
  temp_arr[o[:new_position] - 1] = x
end

some_json_object = {
  "some_array": temp_arr
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...