Я хотел бы простой способ представить порядок списка объектов. Когда объект меняет позицию в этом списке, я хотел бы обновить только одну запись. Я не знаю, можно ли это сделать, но мне интересно спросить SO hive...
Ограничения списка пожеланий
- алгоритм (или структура данных) должен позволять перемещать элементы в списке, обновляя свойства одного элемента
- алгоритм (или структура данных) не должен содержать домашнее хозяйство для поддержания целостности списка.
- алгоритм (или структура данных) должен допускать вставку новых элементов или удаление существующих элементов
Почему я забочусь только об обновлении одного элемента за раз...
[ОБНОВЛЕНО, чтобы уточнить вопрос]
Вариант использования для этого алгоритма - это веб-приложение с CRUDy, находчивым настройкой сервера и чистым (Angular) клиентом.
Хорошей практикой является соблюдение чистых действий CRUD, где это возможно, и делает их более чистым. Если я могу выполнить эту операцию в одном запросе resource#update
, тогда мне не нужен какой-либо дополнительный серверный код для обработки переупорядочения, и все это можно сделать с помощью CRUD без каких-либо изменений.
Если для каждого хода необходимо обновить несколько элементов в списке, мне потребуется новое действие для моего контроллера для его обработки. Это не шоу, но он начинает перетекать в Angular, и все становится менее чистым, чем это должно быть идеально.
Пример
Скажем, у нас есть журнал, и у журнала есть несколько страниц:
Original magazine
- double page advert for Ford (page=1)
- article about Jeremy Clarkson (page=2)
- double page advert for Audi (page=3)
- article by James May (page=4)
- article by Richard Hammond (page=5)
- advert for Volkswagen (page=6)
Вариант 1: сохранение целых номеров страниц
..., в котором мы обновляем до N записей за ход
Если я хочу вытащить страницу Ричарда Хаммонда со страницы 5 на страницу 2, я могу сделать это, изменив номер своей страницы. Однако мне также нужно изменить все страницы, которые он вытесняет:
Updated magazine
- double page advert for Ford (page=1)
- article by Richard Hammond (page=2)(old_value=5)*
- article about Jeremy Clarkson (page=3)(old_value=2)*
- double page advert for Audi (page=4)(old_value=3)*
- article by James May (page=5)(old_value=4)*
- advert for Volkswagen (page=6)
* обновленные свойства
Однако я не хочу обновлять множество записей
- он не соответствует моей архитектуре
Предположим, что это выполняется с помощью переадресации javascript drag-n-drop через Angular.js. В идеале я хотел бы просто обновить значение на странице, которая была перемещена, и оставить остальные страницы в одиночку. Я хочу отправить http-запрос ресурсу CRUD для страницы Ричарда Хаммонда, в котором говорится, что теперь он перемещен на вторую страницу.
- и не масштабируется
Это не проблема для меня, но в какой-то момент у меня может быть 10 000 страниц. Я бы предпочел не обновлять 9999 из них, когда я перемещаю новую страницу на первую страницу.
Вариант 2: связанный список
..., в котором мы обновляем 3 записи за ход
Если вместо хранения позиции страницы я вместо этого храню страницу, которая предшествует ей, я уменьшаю количество действий с максимального числа от N до 3.
Original magazine
- double page advert for Ford (id = ford, page_before = nil)
- article about Jeremy Clarkson (id = clarkson, page_before = ford)
- article by James May (id = captain_slow, page_before = clarkson)
- double page advert for Audi (id = audi, page_before = captain_slow)
- article by Richard Hammond (id = hamster, page_before = audi)
- advert for Volkswagen (id = vw, page_before = hamster)
снова мы перемещаем нахального хомяка...
Updated magazine
- double page advert for Ford (id = ford, page_before = nil)
- article by Richard Hammond (id = hamster, page_before = ford)*
- article about Jeremy Clarkson (id = clarkson, page_before = hamster)*
- article by James May (id = captain_slow, page_before = clarkson)
- double page advert for Audi (id = audi, page_before = captain_slow)
- advert for volkswagen (id = vw, page_before = audi)*
* обновленные свойства
Для этого требуется обновить три строки в базе данных: страницу, которую мы переместили, страницу чуть ниже ее старой позиции и страницу чуть ниже ее новой позиции.
Это лучше, но оно по-прежнему включает в себя обновление трех записей и не дает мне находчивого поведения CRUD, которое я ищу.
Вариант 3: Нецелое позиционирование
..., в котором мы обновляем только 1 запись за ход (но нуждаемся в домашнем хозяйстве)
Помните, однако, я все же хочу обновить только одну запись для каждого повторного позиционирования. В моем стремлении сделать это я придерживаюсь другого подхода. Вместо сохранения позиции страницы в виде целого числа я сохраняю ее как float. Это позволяет мне перемещать элемент, сдвигая его между двумя другими:
Original magazine
- double page advert for Ford (page=1.0)
- article about Jeremy Clarkson (page=2.0)
- double page advert for Audi (page=3.0)
- article by James May (page=4.0)
- article by Richard Hammond (page=5.0)
- advert for Volkswagen (page=6.0)
а затем мы снова перемещаем Hamster:
Updated magazine
- double page advert for Ford (page=1.0)
- article by Richard Hammond (page=1.5)*
- article about Jeremy Clarkson (page=2.0)
- double page advert for Audi (page=3.0)
- article by James May (page=4.0)
- advert for Volkswagen (page=6.0)
* обновленные свойства
Каждый раз, когда мы перемещаем элемент, мы выбираем значение где-то между элементом выше и ниже него (скажем, беря среднее значение двух предметов, которые мы проскальзываем между ними).
В конце концов, хотя вам нужно reset...
Независимо от того, какой алгоритм вы используете для вставки страниц друг в друга, в конечном итоге будет выведено из десятичных знаков, поскольку вам нужно продолжать использовать меньшие числа. По мере того, как вы перемещаете элементы все больше и больше раз, вы постепенно перемещаетесь по цепочке с плавающей запятой и в конечном итоге нуждаетесь в новой позиции, которая меньше, чем что-либо доступное.
Время от времени вам нужно сделать reset, чтобы переиндексировать список и вернуть его обратно в пределах диапазона. Это нормально, но мне интересно узнать, есть ли способ кодировать порядок, который не требует этого домашнего хозяйства.
Есть ли алгоритм, который требует только 1 обновления и без обслуживания?
Существует ли для этой проблемы алгоритм (или, точнее, кодирование данных), который требует только одного обновления и без учета домашнего хозяйства? Если это так, вы можете объяснить это на простом английском языке, как это работает (например, нет ссылки на ориентированные графики или вершины...)? Muchos gracias.
ОБНОВЛЕНИЕ (награда за пост-очки)
Я наградил щедрость на этом вопросе, который, как я чувствую, получил самый интересный ответ. Никто не смог предложить решение (поскольку из-за внешнего вида вещей его нет), поэтому я не задал какой-либо конкретный вопрос как правильный.
Настройка критерия без ведома
После того, как мы потратили еще больше времени на размышления об этой проблеме, мне приходит в голову, что критерий домашнего хозяйства должен быть скорректирован. Настоящая опасность с домашним хозяйством заключается не в том, что это хлопот, а в том, что она идеально должна быть надежной для клиента, у которого есть выдающаяся копия набора до дома.
Скажем, что Джо загружает страницу, содержащую список (используя Angular), а затем уходит, чтобы сделать чашку чая. Сразу после того, как он загружает его, происходит уборка и пересчет всех предметов (1000, 2000, 3000 и т.д.). После того, как он вернулся с чашки чая, он перемещает предмет с 1010 1011. В этот момент существует риск, что переиндексация поместит его предмет в положение, в которое оно не предназначалось.
В качестве примечания для будущего - любой алгоритм домашнего хозяйства должен в идеале быть надежным для элементов, представленных в разных версиях версии houseexpt. В качестве альтернативы вам следует обновить домашнее хозяйство и создать ошибку, если кто-то попытается обновить версии.
Проблемы со связанным списком
В то время как связанный список требует только нескольких обновлений, он также получил некоторые недостатки:
- Это не тривиально иметь дело с удалениями из списка (и вам, возможно, придется соответствующим образом настроить ваш метод #destroy
- Нелегко заказать список для извлечения
Метод, который я выбрал бы
Я думаю, что, увидев все обсуждения, я думаю, что я бы выбрал нецелое (или строковое) позиционирование:
- он надежно вставляет и удаляет
- он работает с одним обновлением
Тем не менее, он нуждается в домашнем хозяйстве и, как уже упоминалось выше, если вы собираетесь завершить, вам также понадобится обновить каждое домашнее хозяйство и вызвать ошибку, если кто-то попытается обновить его на основе предыдущей версии.