I have the array of dates and I want to group dates by year, month, week, day, 6h, 8h and 1h and sum value. for example, I have the following array:
const data = [
{ x: "2021-10-17T14:38:45.540Z", y: 2 },
{ x: "2021-09-16T14:36:46.540Z", y: 1 },
{ x: "2021-01-04T14:35:46.540Z", y: 2 },
{ x: "2021-01-01T14:30:46.540Z", y: 1 },
{ x: "2020-02-01T06:28:47.520Z", y: 12 },
{ x: "2020-02-01T07:28:47.520Z", y: 12 },
// ...
{ x: "2019-04-13T10:19:20.034Z", y: 20 },
// ...
{ x: "2018-01-01T09:09:19.134Z", y: 4 },
{ x: "2017-01-01T12:09:19.034Z", y: 11 },
{ x: "2016-01-02T12:10:20.034Z", y: 24 },
// ...
]
This is what I tried using momentjs and lodash Group array of object by date
for year I got this result and the problem some years like 2018 and 2016 is not displayed:
[
{
"color": "Blue",
"value": 6,
"label": "2021"
},
{
"color": "Blue",
"value": 24,
"label": "2020"
},
{
"color": "Blue",
"value": 1212,
"label": "2019"
},
{
"color": "Blue",
"value": 11,
"label": "2017"
}
]
Expected output for year:
[
{
"color": "Blue",
"value": 6,
"label": "2021"
},
{
"color": "Blue",
"value": 24,
"label": "2020"
},
{
"color": "Blue",
"value": 1212,
"label": "2019"
},
{
"color": "Blue",
"value": 10,
"label": "2018"
},
{
"color": "Blue",
"value": 11,
"label": "2017"
},
{
"color": "Blue",
"value": 48,
"label": "2016"
}
]
This can be achieved with a standard 'group-by' using reduce, here accumulating into an object and returning an array using Object.values().
I've declared a simple get_date_parts helper to parse the ISO date strings which should be ample for grouping, but you can use a Date object for more complicated label formats if need be.
Here is the year grouping, the pattern can be adapted to all your other groupings, though you'll need to do a little arithmetic to determine hour ranges.
const data = [{ x: '2021-10-17T14:38:45.540Z', y: 2 }, { x: '2021-09-16T14:36:46.540Z', y: 1 }, { x: '2021-01-04T14:35:46.540Z', y: 2 }, { x: '2021-01-01T14:30:46.540Z', y: 1 }, { x: '2020-02-01T06:28:47.520Z', y: 12 }, { x: '2020-02-01T07:28:47.520Z', y: 12 }, { x: '2019-04-13T10:19:20.034Z', y: 20 }, { x: '2018-01-01T09:09:19.134Z', y: 4 }, { x: '2017-01-01T12:09:19.034Z', y: 11 }, { x: '2016-01-02T12:10:20.034Z', y: 24 },];
function get_date_parts(iso_string) {
const [year, month, day, hr, min, sec] = iso_string.split(/\D/g);
return { year, month, day, hr, min, sec };
}
function group_by_year(arr) {
return Object.values(
arr.reduce((a, { x: date_string, y: value }) => {
const { year } = get_date_parts(date_string);
(a[year] ??= { color: 'Blue?', value: 0, label: year }).value += value;
return a;
}, {}),
);
}
const grouped_by_year = group_by_year(data).sort((a, b) => +b.label - +a.label);
console.log(grouped_by_year);
or by month, also showing an alternative to logical nullish assignment (??=) for initial assignment to a[key].
const data = [{ x: '2021-10-17T14:38:45.540Z', y: 2 }, { x: '2021-09-16T14:36:46.540Z', y: 1 }, { x: '2021-01-04T14:35:46.540Z', y: 2 }, { x: '2021-01-01T14:30:46.540Z', y: 1 }, { x: '2020-02-01T06:28:47.520Z', y: 12 }, { x: '2020-02-01T07:28:47.520Z', y: 12 }, { x: '2019-04-13T10:19:20.034Z', y: 20 }, { x: '2018-01-01T09:09:19.134Z', y: 4 }, { x: '2017-01-01T12:09:19.034Z', y: 11 }, { x: '2016-01-02T12:10:20.034Z', y: 24 },];
function get_date_parts(iso_string) {
const [year, month, day, hr, min, sec] = iso_string.split(/\D/g);
return { year, month, day, hr, min, sec };
}
function group_by_month(arr) {
return Object.values(
arr.reduce((a, { x: date_string, y: value }) => {
const { year, month } = get_date_parts(date_string);
const key = `${year}/${month}`;
// using logical nullish assignment
//(a[key] ??= { color: 'Blue?', value: 0, label: key }).value += value;
// or written out long hand
if (a[key] === undefined) {
a[key] = { color: 'Blue?', value: 0, label: key };
}
a[key].value += value;
return a;
}, {}),
);
}
const grouped_by_month = group_by_month(data).sort((a, b) => b.label.localeCompare(a.label));
console.log(grouped_by_month);
For the groupings that are not directly represented in the date you can use some simple arithmetic.
function get_date_parts(iso_string) {
const [year, month, day, hr, min, sec] = iso_string.split(/\D/g);
return { year, month, day, hr, min, sec };
}
const date_string = '2020-02-07T07:28:47.520Z';
const { year, month, day, hr } = get_date_parts(date_string);
// week in the month
const week = Math.floor((parseInt(day, 10) - 1) / 7);
const week_label = `${year}/${month} - week ${week + 1}`;
console.log({ week_label, week });
// hour range
const range_size = 8;
const range = Math.floor(parseInt(hr, 10) / range_size);
// range times
const range_start = `${(range * range_size).toString().padStart(2, '0')}:00`;
const range_end = `${(range * range_size + range_size).toString().padStart(2, '0')}:00`;
const range_label = `${day}/${month}/${year} ${range_start}-${range_end}`;
console.log({ range_label, range });
I am creating a simple platformer. I am trying to create collisions with objects and be able to detect those. With the code I have below I am not able to detect collisions properly and stop the player from moving when they collide. What is supposed to happen is the code is supposed to check if there is a collision with any of the objects in the level.Objects array. The code I have now does not detect collisions and you fall infinity into the ground. How would I create a function that detects collisions properly and returns true on which side it collides with?
function runGame() {
var game = document.getElementById('game')
var ctx = game.getContext("2d")
var DonaldRest = document.getElementById('DonaldRest')
var GrassTile = document.getElementById('GrassTile')
var gravity = 0.5
var momentum = 0;
var momentumDown = 0;
var spacing = 64;
var speed = 2;
var maxSpeed = 2;
var jumpHeight = 3;
var levels = [{
Name: "Level 1",
Objects: [{
Type: "GrassFloor",
Location: {
x: 0,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 1,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 2,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 3,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 4,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 5,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 6,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 7,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 8,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 9,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 10,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 11,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 12,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 13,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 14,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 15,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 16,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 17,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 18,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 19,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 20,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 21,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 22,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 23,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 24,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 25,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 26,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 27,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 28,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, {
Type: "GrassFloor",
Location: {
x: spacing * 29,
y: 0
},
Scale: {
x: 1,
y: 1
},
Solid: true,
Height: 3
}, ]
}]
var player = {
position: {
x: 0,
y: 0
},
Time: 0
}
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
var game = setInterval(function() {
ctx.imageSmoothingEnabled = false
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
ctx.fillStyle = "#adfffa"
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height)
ctx.drawImage(DonaldRest, ctx.canvas.width / 2 - (96 / 2), ctx.canvas.height / 2 - (96 / 2), 96, 96)
var Level = levels[0]
var Objects = Level.Objects
var OnGround = checkCollisions().Bottom
if (OnGround == false) {
if (momentumDown <= maxSpeed) {
momentumDown -= gravity;
player.position.y += momentumDown;
} else {
player.position.y += momentumDown;
}
} else {
momentumDown = 0;
console.log("collided")
}
for (var j = 0; j < Objects.length; j++) {
if (Objects[j].Type == "GrassFloor") {
ctx.drawImage(GrassTile, Objects[j].Location.x - player.position.x, (ctx.canvas.height - spacing + player.position.y) - (spacing * Objects[j].Height), spacing, spacing)
for (var i = -5; i < Objects[j].Height; i++) {
ctx.drawImage(DirtTile, Objects[j].Location.x - player.position.x, (ctx.canvas.height - spacing) - (i * spacing) + player.position.y, spacing, spacing)
}
}
}
}, 17); //17
$(document).keydown(function(e) {
if (e.which == 32) {
if (checkCollisions().Bottom == true) {
console.log(momentumDown);
momentumDown -= jumpHeight
console.log(momentumDown);
}
}
})
function isTouchingFloor(e1, e2) {
return e1.x < (e2.x + e2.w) && (e1.x + e1.w) > e2.x && e1.y - momentumDown < (e2.y + e2.h) && (e1.y - momentumDown + e1.h) > e2.y;
}
function checkCollisions() {
var Objects = levels[0].Objects;
var Collision = {
Top: false,
Left: false,
Bottom: false,
Right: false
}
var GrassTileImg = new Image()
var o1 = {
y: player.position.y,
h: 96,
x: player.position.x,
w: 96
}
for (var i = 0; i < Objects.length; i++) {
var o2 = {
y: Objects[i].Location.y,
x: Objects[i].Location.x,
h: 64,
w: 64
}
if (isTouchingFloor(o1, o2) == true) {
Collision.Bottom == true;
}
console.log(Collision.Bottom)
}
return Collision
}
}
This is a snippet of code I used when I was doing a group project for checking collisions. Essentially we make a function to check the collision between two objects and the corresponding side.
Collision Function
/**
* Checks for a collision of two objects. Moves objectA if side is a string with the word move.
* #param objectA The object that needs to move.
* #param objectB The object that needs to block.
* #param side If true, makes return a string. If "move", moves objectA.
* #returns {*} String if side evaluates to true, otherwise boolean.
*/
function checkCollision(objectA, objectB, side) {
if (side) {
var vx = objectA.centerX() - objectB.centerX(),
vy = objectA.centerY() - objectB.centerY(),
combinedHalfWidths = objectA.halfWidth() + objectB.halfWidth(),
combinedHalfHeights = objectA.halfHeight() + objectB.halfHeight(),
collisionSide = "";
if (Math.abs(vx) < combinedHalfWidths && Math.abs(vy) < combinedHalfHeights) {
var overlapX = combinedHalfWidths - Math.abs(vx),
overlapY = combinedHalfHeights - Math.abs(vy);
if (overlapX > overlapY) {
if (vy > 0) {
if (side === "move") {
objectA.vy = objectB.vy;
objectA.y += overlapY;
}
collisionSide = "top";
} else {
if (side === "move") {
objectA.vy = objectB.vy;
objectA.y -= overlapY;
}
collisionSide = "bottom";
}
} else {
if (vx > 0) {
if (side === "move") {
objectA.vx = objectB.vx;
objectA.x += overlapX;
}
collisionSide = "left";
} else {
if (side === "move") {
objectA.vx = objectB.vx;
objectA.x -= overlapX;
}
collisionSide = "right";
}
}
}
return collisionSide;
} else {
return !(objectA.x + objectA.width < objectB.x ||
objectB.x + objectB.width < objectA.x ||
objectA.y + objectA.height < objectB.y ||
objectB.y + objectB.height < objectA.y);
}
}
This function does the checking for all objects we have loaded in our level.
Collision Checking
function doCollisionChecks() {
var checkPush = false;
player.isOnGround = false;
for (var i = 0; i < solids.length; i++) {
if (checkCollision(player, solids[i], "move") === "bottom") {
player.isOnGround = true;
player.state = player.STANDING;
if (solids[i].vx) {
if (solids[i].vx !== player.extraVX) {
player.extraVX = solids[i].vx
}
} else {
player.extraVX = 0;
}
}
}
for (i = 0; i < objects.length; i++) {
if (checkCollision(objects[i], player, true) === "right" || checkCollision(objects[i], player, true) === "left") {
player.speedLimit = scaleWidth(2);
objects[i].speedLimit = scaleWidth(2);
checkPush = true;
}
//Letting the player move boxes, while avoiding a "box hop" bug.
if (checkCollision(objects[i], player, true) === "top") {
player.y = objects[i].y - player.height;
} else if (checkCollision(objects[i], player, true) === "bottom") {
player.y = objects[i].y + objects[i].height;
} else if (player.centerY() > objects[i].y + objects[i].height * 0.1 && player.centerY() < objects[i].y + objects[i].height * 0.9) {
checkCollision(objects[i], player, "move");
}
for (var j = 0; j < solids.length; j++) {
if (checkCollision(objects[i], solids[j], "move") === "bottom" && solids[j].vx) {
objects[i].extraVX = solids[j].vx;
} else {
objects[i].extraVX = 0;
}
checkCollision(objects[i], solids[j], "move");
}
for (j = 0; j < objects.length; j++) {
if (j !== i) {
//Avoids boxes falling through one another.
if (checkCollision(objects[i], objects[j], true) === "top") {
checkCollision(objects[j], objects[i], "move");
} else {
checkCollision(objects[i], objects[j], "move");
}
}
}
if (checkCollision(player, objects[i], true) === "bottom") {
player.isOnGround = true;
player.state = player.STANDING;
player.extraVX = objects[i].extraVX;
} else if (checkCollision(player, objects[i], true) === "top") {
score -= 50;
gameState = OVER;
}
checkCollision(player, objects[i], "move");
if (objects[i].y > c.height) {
if (objects[i].correct) {
score += 100;
objects.splice(i, 1);
checkWin();
} else {
fRed = 0;
score -= 100;
objects.splice(i, 1);
}
}
}
for (i = 0; i < enemies.length; i++) {
if (checkCollision(enemies[i], player)) {
score -= 50;
gameState = OVER;
}
j = 0;
while (enemies[i] && j < objects.length) {
if (checkCollision(objects[j], enemies[i], true) === "bottom") {
score += 75;
objects[j].vy = -1 * scaleHeight(6);
enemies.splice(i, 1);
//score++
}
j++;
}
}
if (checkCollision(player, powerUp)) {
score += 25;
activatePowerUp();
}
if (!checkPush) {
player.speedLimit = scaleWidth(3);
for (i = 0; i < objects.length; i++) {
objects[i].speedLimit = scaleWidth(3);
}
}
}
Sorry but there is a lot of irrelevant attributes used such as speed limits etc. But it works correctly.
You can find the whole source here.
So I'm using the Rickshaw graphing library and I was wondering how to dynamically add points to a graph.
I have a graph instantiated like this:
#seriesData = [ [], [], [] ]
random = new Rickshaw.Fixtures.RandomData(150)
for (var i = 0; i < 50; i++) {
random.addData(self.seriesData)
}
#graph = new Rickshaw.Graph(
element: document.getElementById("chart")
width: 550
height: 300
renderer: 'area'
series: [
{
color: "#c05020"
data: self.seriesData[0]
name: 'One'
}, {
color: "#30c020"
data: self.seriesData[1]
name: 'Two'
}, {
color: "#2791d7"
data: self.seriesData[2]
name: 'Three'
}
]
)
#graph.render()
hoverDetail = new Rickshaw.Graph.HoverDetail(
graph: self.graph
)
legend = new Rickshaw.Graph.Legend(
graph: self.graph
element: document.getElementById('legend')
)
shelving = new Rickshaw.Graph.Behavior.Series.Toggle(
graph: self.graph
legend: legend
)
axes = new Rickshaw.Graph.Axis.Time(
graph: self.graph
)
axes.render()
And I have data coming in through socket.io like this:
app.on('data',
(one, two, three) =>
// Dynamically add data points to graph
)
And I was wondering how to append these three points to the graph. I can't find any good documentation for this library. I know it's built on top of d3.js, but I'm not sure how to incorporate these methods into my graph.
Any help would be appreciated.
I envision two scenario that could solve your question:
Using the fixed Window Series for Streaming Data
leveraging the fact that arrays in javascript are passed by reference. A demo is available here
.
var data = [
{
data: [ { x: 0, y: 120 }, { x: 1, y: 890 }, { x: 2, y: 38 }, { x: 3, y: 70 }, { x: 4, y: 32 } ],
color: "#c05020"
}, {
data: [ { x: 0, y: 80 }, { x: 1, y: 200 }, { x: 2, y: 100 }, { x: 3, y: 520 }, { x: 4, y: 133 } ],
color: "#30c020"
}
];
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
renderer: 'line',
height: 300,
width: 800,
series: data
} );
var y_ticks = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
element: document.getElementById('y_axis'),
} );
graph.render();
$('button#add').click(function() {
data.push({
data: [ { x: 0, y: 200 }, { x: 1, y: 390 }, { x: 2, y: 1000 }, { x: 3, y: 200 }, { x: 4, y: 230 } ],
color: "#6060c0"
});
graph.update();
});