Here is my solution: # one day One-day calendar
Requirements
Part I: Write a function for laying out a series of events in a calendar for one day.
Events will be placed in the container. The top of the container is 9am and the bottom is 9pm.
The width of the container will be 620px (10px padding left and right), and the height will be 720px (1 pixel for every minute between 9:00 and 21:00). Objects must be positioned so that they do not visually overlap. If there is only one event in a given time interval, its width should be 600 pixels.
There are two main limitations: 1. Each counter event must be the same width as every other event that it collides with the width. 2. The event should use the maximum possible width, still adhering to the first constraint.
The following is an example image. 
Function input will be an array of event objects with the start and end time of the event. Example (JS):
[ {id : 1, start : 60, end : 120},
The function should return an array of event objects that have left and top positions (relative to the top left corner of the container), in addition to id, start and end.
Part II Use your function from part I to create a web page that is written in style, as shown in the example below.
with the following calendar events:
- An event that starts at 9:30 and ends at 11:30.
- An event that starts at 18:00 and ends at 19:00.
- An event that starts at 18:20 and ends at 19:20.
- An event that starts at 19:10 p.m. and ends at 8:10 p.m.
the code
Installation and launch
- Clone this repo
- Open the index.html file in your favorite browser.
Note: at startup there is a set of events by default (required in the second part).
For testing, right below the default array (line 14), you can find the generateEvents function that generates a random array of events. The size of the array will be determined by the arrayLength attribute.
Dependencies
does not have!
Decision
Below you can find an algorithm to solve the problem in accordance with the requirements.
Introduction
I will try to solve this problem in the form of graphs, so I need to specify several terms.
Conditions
Node: represents an event - $ n $, $ n \ in N, N $ - a group of all nodes.
Edge: represents counter events - $ e $, $ e \ in E, E $ - a group of all edges. For example, if node $ u $ and $ v $ collide, then the edge $ e_ {u, v} $ will be connected.
Graph: a set of nodes and edges $ G, G \ in (N, E) $.
Cluster: represents a group of connected nodes (a subgroup of the diagram) - $ c $, $ c \ subseteq G $. For example, if we have the following nodes: $ u, v, w $ and edge $ e_ {u, v} $. Then there will be 2 clusters, the first will contain $ u, v $, and the second will contain only $ w $.
Clique: represents a subgroup of nodes in a cluster, each pair of nodes in this group has a connecting edge - $ cq $, $ cq \ subseteq c $. Note: a click is a group of colliding events.
Tip: A container of the day that contains all the events.
Condition example
For the following input:
[ {id : 1, start : 0, end : 120}, {id : 2, start : 60, end : 120}, {id : 3, start : 60, end : 180}, {id : 4, start : 150, end : 240}, {id : 5, start : 200, end : 240}, {id : 6, start : 300, end : 420}, {id : 7, start : 360, end : 420}, {id : 8, start : 300, end : 720} ]
The schedule will be:

Black loop - node - event
Green ellipse - clique - group of colliding events
Red ellipse - cluster - group of connected nodes
Blue line - edge - connector between oncoming events
Note: the upper left green ellipse is the largest click in the left cluster.
The fee will be: 
Red Rectangle - Cluster
Colored dots - click (each color - another click).
Paraphrase paraphrase
- Each node in the same cluster must have the same width on the board in order to meet the first limitation.
- The nodes should not intersect with each other on the board, but starch to the maximum width and at the same time adhere to the first restriction.
- The width of the nodes in the cluster will be set by the largest click in the cluster. This is true because the nodes in one click will share at least one minute on the board, i.e. they must have the same width (because they collide). Thus, the other nodes in the cluster will have the same width as the smallest node.
- Each node in a click will receive its X axis position relative to its neighbors.
Algorithm
For this array of events arrayOfEvents (from the sample requirements):
[ {id : 1, start : 60, end : 120},
Step one: create a histogram of events.
An array of arrays will be created that will call this array as a histogram . The histogram length will be 720, each histogram index will represent a minute on the board (day). Lets name each histogram a minute index. Each minute is an array. Each minute array index represents an event that occurs at that minute.
pseudo code:
histogram = new Array(720); forEach minute in histogram: minute = new Array(); forEach event in arrayOfEvents: forEach minute inBetween event.start and endMinute: histogram[minute].push(event.id);
histogram array will look like this after this step (for this example):
[ 1: [], 2: [], . . . 59: [], 60: [1], 61: [1], . . . 99: [1], 100: [1,2], 101: [1,2], . . . 120: [1,2], 121: [2], 122: [2], . . . 240: [2], 241: [], 242: [], . . . 699: [], 700: [3], 701: [3], . . . 720: [3] ]
Step two: create a schedule
At this point, a graph will be created, including nodes, node neighbors and clusters, and the largest click of the cluster will be determined.
Note that there will be no edge object, each node will contain a map of nodes (key: node id, value: node) that it encounters (neighbors). This card will be called neighbors. In addition, the maxCliqueSize attribute will be added to each node. maxCliqueSize is the largest click of which node is a part.
pseudo code:
nodesMap := Map<nodeId, node>; graph := Object<clusters, nodesMap>; Node := Object<nodeId, start, end, neighbours, cluster, position, biggestCliqueSize>; Cluster := Object<mapOfNodesInCluster, width> //creating the nodes forEach event in arrayOfEvents { node = new Node(event.id, event.start, event.end, new Map<nodeId, node>, null) nodeMap[node.nodeId] = node; } //creating the clusters cluster = null; forEach minute in histogram { if(minute.length > 0) { cluster = cluster || new Cluster(new Array(), 0); forEach eventId in minute { if(eventId not in cluster.nodes) { cluster.nodes[eventId] = nodeMap[eventId]; nodeMap[eventId].cluster = cluster; } } } else { if(cluster != null) { graph.clusters.push(cluster); } cluster = null; } } //adding edges to nodes and finding biggest clique for each node forEach minute in histogram { forEach sourceEventId in minute { sourceNode = eventsMap[sourceEventId]; sourceNode.biggestCliqueSize = Math.max(sourceNode.biggestCliqueSize, minute.length); forEach targetEventId in minute { if(sourceEventId != targetEventId) { sourceNode.neighbours[targetEventId] = eventsMap[targetEventId]; } } } }
Step Three: Calculate the Width of Each Cluster
As mentioned above, the width of all nodes in the cluster will be determined by the size of the largest click in the cluster.
The width of each node $ n $ in the cluster $ c $ will follow this equation:
$$ n_ {width} = \ frac {Board_ {width}} {Max \ left (n_ {1} .biggestCliqueSize, n_ {2} .biggestCliqueSize, ..., n_ {n} .biggestCliqueSize \ right)} $$
Each node width will be set in the cluster to which it is associated. Thus, the width property will be set to the cluster object.
pseudo code:
forEach cluster in graph.clusters { maxCliqueSize = 1; forEach node in cluster.nodes { maxCliqueSize = Max(node.biggestCliqueSize, sizeOf(node.clique); } cluster.width = BOARD_WIDTH / maxCliqueSize; cluster.biggestCliqueSize = biggestCliqueSize; }
Fourth step: calculates the position of the node in its click.
As already mentioned, nodes will have to share the X axis ("real estate") with their neighbors. At this point, the position of the X axis will be indicated for each node according to its neighbors. The largest click in the cluster will determine the number of available places.
pseudo code:
forEach node in nodesMap { positionArray = new Array(node.cluster.biggestCliqueSize); forEach cliqueNode in node.clique { if(cliqueNode.position != null) { //marking occupied indexes positionArray[cliqueNode.position] = true; } } forEach index in positionArray { if(!positionArray[index]) { node.position = index; break; } } }
Step Five: Placing the nodes on the board. At this stage, we already have all the information necessary to place an event (node) at its position on the board. The position and size of each node will be determined using:
- height: node.end - node.start
- width: node.cluster.width
- top offset: node.start
- left shift: node.cluster.width * node.position + left-padding
Algorithm complexity
The time complexity of the algorithm is $ O \ left (n ^ {2} \ right) $.
The complexity of the algorithm space is $ O \ left (n \ right) $.
Github repo: https://github.com/vlio20/one-day