Поиск алгоритма для эффективного размещения баннеров календарных событий

Я ищу алгоритм для эффективного размещения баннеров на целый день / многодневных событий, во многом аналогично просмотру месяца в Outlook или Календаре Google. У меня есть несколько событий с датой начала и окончания, упорядоченные по возрастанию даты начала (а затем окончания) (или в любом другом порядке, пожалуйста, я собираю события из таблицы базы данных). Я хотел бы минимизировать средний объем используемого вертикального пространства, потому что после баннеров событий мне нужно будет разместить другие события только на этот день (они всегда идут после баннеров на заданную дату). Так, например, если бы у меня было два события, одно 1 / 10-1 / 11 и одно 1 / 11-1 / 15, я бы предпочел расположить их так (каждый столбец - один день):

 bbbbb
aa

и не нравится:

aa
 bbbbb

потому что, когда я добавляю события только на день (x, y и z), я могу это сделать (я бы предпочел первое, не хочу второе):

 bbbbb    vs.    aa
aa xyz            bbbbb
                    xyz

Но это не так просто, как сначала разместить более длинные события, потому что с 1 / 10-1 / 11, 1 / 13-1 / 14 и 1 / 11-1 / 13 я хотел бы:

aa cc
 bbb

в отличие от:

 bbb
aa cc

потому что это позволило бы событиям x и y:

aa cc    vs.     bbb
xbbby           aa cc
                x   y

И, конечно, я бы предпочел сделать это за один проход. Для структуры данных я сейчас использую карту от даты к списку, где для каждого дня события я добавляю событие в соответствующий список. Таким образом, трехдневное событие отображается в трех списках, каждый под одним из дней на карте. Это удобная структура для преобразования результата в визуальный вывод, но я открыт и для других структур данных. В настоящее время я использую жадный алгоритм, в котором я просто добавляю каждое событие по порядку, но это может привести к нежелательным артефактам, например:

aa ccc          
 bbbbb
    dd
     eeeeeeeeeeeeeeeee

Это тратит много пространства на большую часть дней «е» событий.

Любые идеи?

Я не совсем понимаю разницу между примерами 1 и 2 и 3 и 4. Что касается примера 5 - я не вижу другого способа организации событий, который занимал бы меньше вертикального пространства. Поскольку никакие два из b / c / d / e в любом случае не могут быть на одной линии, поэтому потребуется как минимум 4 строки.

Vilx- 30.12.2008 17:23

Вы правы, мои намерения были совершенно не ясны. Я только что внес некоторые правки, которые, надеюсь, сделают его более понятным.

Hilton Campbell 30.12.2008 17:30

Для №5 проблема заключается в том, что короткие события всегда идут ниже длинных, поэтому наличие «е» внизу означает, что добавление короткого события к любому из дней «е» добавит пятую строку. Если е было в верхней строке, то короткие события можно было бы добавить к любому дню, кроме дней «d», без использования дополнительной строки.

Sparr 30.12.2008 23:28
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
3 241
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я думаю, что в такой ситуации вам гораздо лучше сначала убедиться, что ваши данные правильно организованы, а затем отрендерить их. Я знаю, что вам нужен один проход, но я думаю, что результаты будут намного лучше.

Например, организуйте данные в строки, которые вам понадобятся для данного дня, и организуйте события наилучшим образом, начиная с самых длинных событий (не нужно отображать в первую очередь, но они должны быть организованы первый) и переходя к самым коротким событиям. Это позволит вам соответствующим образом визуализировать свой вывод, не тратя впустую места и избегая тех дней «е» событий. Дополнительно тогда:

 bbb
aa cc

или же

aa cc
 bbb

не имеет значения, потому что x и y всегда могут идти по обе стороны от bbb или даже между aa и cc.

Надеюсь, вы найдете это полезным.

Возможно, мне придется сделать более одного прохода, что нормально, если неизбежно. Позиции x, y и z (события, которые происходят в течение определенного дня) фиксируются после размещения баннеров событий. Они должны быть размещены в соответствующий день, и они должны быть размещены после баннеров мероприятия на этот день.

Hilton Campbell 30.12.2008 18:06
Ответ принят как подходящий

Вот общий набросок одного из возможных решений (с использованием целых чисел дня недели вместо полноценных дат). Этот интерфейс:

public interface IEvent {

    public abstract int getFirst();  // first day of event
    public abstract int getLast();   // last day of event
    public abstract int getLength(); // total number of days
    public abstract char getLabel(); // one-char identifier

    // true if this and that have NO days in common
    public abstract boolean isCompatible(IEvent that);

    // true if this is is compatible with all events
    public abstract boolean isCompatibleWith(Collection<IEvent> events);

}

должен быть реализован для использования алгоритма, описанного в методе layout ниже.

Кроме того, конкретный класс должен реализовывать Comparable для создания естественного порядка, в котором более длинные события предшествуют более коротким. (В моем примере реализации для демонстрации ниже использовался порядок убывания длины, затем восходящей даты начала, затем восходящей метки.)

Метод layout принимает коллекцию экземпляров IEvent и возвращает Map, который назначает каждой строке в презентации набор событий, которые могут отображаться в этой строке.

public Map<Integer,Set<IEvent>> layout(Collection<IEvent> events) {
    Set<IEvent> remainingEvents = new TreeSet<IEvent>(events);
    Map<Integer,Set<IEvent>> result = new TreeMap<Integer,Set<IEvent>>();
    int day = 0;
    while (0 < remainingEvents.size()) {
        Set<IEvent> dayEvents = new TreeSet<IEvent>();
        for(IEvent e : remainingEvents) {
            if (e.isCompatibleWith(dayEvents)) {
                dayEvents.add(e);
            }
        }
        remainingEvents.removeAll(dayEvents);
        result.put(day, dayEvents);
        ++day;
    }
    return result;
}

Каждая строка состоит из выбора самого длинного оставшегося события и постепенного выбора всех дополнительных событий (в порядке, описанном выше), которые совместимы с ранее выбранными событиями для текущей строки. В результате все события «плывут» вверх, насколько это возможно, без столкновений.

В следующей демонстрации показаны два сценария в вашем вопросе, а также случайно созданный набор событий.

Event collection:
    x(1):4
    b(5):2..6
    y(1):5
    a(2):1..2
    z(1):6
Result of layout:
    0 -> {b(5):2..6}
    1 -> {a(2):1..2, x(1):4, y(1):5, z(1):6}
Visual presentation:
      bbbbb
     aa xyz

Event collection:
    x(1):1
    b(3):2..4
    a(2):1..2
    c(2):4..5
    y(1):5
Result of layout:
    0 -> {b(3):2..4, x(1):1, y(1):5}
    1 -> {a(2):1..2, c(2):4..5}
Visual presentation:
     xbbby 
     aa cc 

Event collection:
    f(2):1..2
    h(2):1..2
    d(4):1..4
    e(4):2..5
    c(1):6
    a(2):5..6
    g(4):2..5
    b(2):0..1
Result of layout:
    0 -> {d(4):1..4, a(2):5..6}
    1 -> {e(4):2..5, b(2):0..1, c(1):6}
    2 -> {g(4):2..5}
    3 -> {f(2):1..2}
    4 -> {h(2):1..2}
Visual presentation:
     ddddaa
    bbeeeec
      gggg 
     ff    
     hh    

Другие вопросы по теме