I have to display around 20 lines within a line chart with Google's Line Chart. It might happen, that these lines overlap. What is the best way to add noise to the data so all the lines are visible.
the values for cat1, cat2 and cat3 are the same but I want that the it is evident from the image, that they are really close - so my idea is that the lines should not overlap but be a bit apart. The user cannot assume, that all the values overlap since for some events, let's say D, the values might be missing.
Given this chart:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['x', '#1', '#2', '#3'],
['A', 1, 1, 1],
['B', 2, 2, 2],
['C', 3, 3, 3],
['D', 4, 4, 4],
['E', 5, 5, 5],
['F', 6, 6, 6],
['G', 7, 7, 7],
['H', 8, 8, 8],
['I', 9, 9, 9],
]);
// Create and draw the visualization.
new google.visualization.LineChart(document.getElementById('visualization')).
draw(data, {width: 500, height: 400,
vAxis: {maxValue: 10}}
);
}
One way is just to add a consistent +/- per series:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['x', '#1', '#2', '#3'],
['A', 1, 1, 1],
['B', 2, 2, 2],
['C', 3, 3, 3],
['D', 4, 4, 4],
['E', 5, 5, 5],
['F', 6, 6, 6],
['G', 7, 7, 7],
['H', 8, 8, 8],
['I', 9, 9, 9],
]);
for (var i = 1;i < data.getNumberOfColumns();i++) {
// Algorithm to add +/- 0.1 for each series
var dither = Math.round((i - 1)/2)/5;
if ( (i - 1) % 2 == 0 ) {
dither = dither * -1;
}
for (var j = 0;j < data.getNumberOfRows();j++){
// Add dither to series to display differently, but keep same data for tooltip
data.setCell(j, i, data.getValue(j, i) + dither, data.getValue(j, i) + '', undefined)
}
}
// Create and draw the visualization.
new google.visualization.LineChart(document.getElementById('visualization')).
draw(data, {width: 500, height: 400,
vAxis: {maxValue: 10}}
);
}
The issue with this method is that if the values of your axes or data change significantly, you won't be able to see the gap (because the resolution of the screen won't be big enough). To get around this issue, we would need to manually set the min/max values of the axes in order to be able to come up with an appropriate factor. For instance, from this answer we can take the following algorithm to determine min and max axes values that approximate what google will set for us automagically:
// Take the Max/Min of all data values in all graphs
var totalMax = 345;
var totalMin = -123;
// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));
// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);
// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;
// Determine the range of your values
var range = newMax - newMin;
// Define the number of gridlines (default 5)
var gridlines = 5;
// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);
// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;
// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;
We can add this all together, and see that it will even work if all the values are increased in factor by 10 (which wouldn't have worked with the hard-coded version):
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['x', '#1', '#2', '#3'],
['A', 10, 10, 10],
['B', 20, 20, 20],
['C', 30, 30, 30],
['D', 40, 40, 40],
['E', 50, 50, 50],
['F', 60, 60, 60],
['G', 70, 70, 70],
['H', 80, 80, 80],
['I', 90, 90, 90],
]);
// Get max and min values for the data table
var totalMin = data.getValue(0,1);
var totalMax = data.getValue(0,1);
for (var i = 1;i < data.getNumberOfColumns();i++) {
for (var j = 0;j < data.getNumberOfRows();j++){
if ( data.getValue(j, i) < totalMin ) {
totalMin = data.getValue(j, i);
}
if ( data.getValue(j, i) > totalMax ) {
totalMax = data.getValue(j, i);
}
}
}
// Calculate grid line axes and min/max settings
// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));
// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);
// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;
// Determine the range of your values
var range = newMax - newMin;
// Define the number of gridlines (default 5)
var gridlines = 5;
// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);
// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;
// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;
// Calculate Dither
for (var i = 1;i < data.getNumberOfColumns();i++) {
// Algorithm to add +/- 0.1 for each series
var dither = Math.round((i - 1)/2)/(10/newInterval);
if ( (i - 1) % 2 == 0 ) {
dither = dither * -1;
}
for (var j = 0;j < data.getNumberOfRows();j++){
// Add dither to series to display differently, but keep same data for tooltip
data.setCell(j, i, data.getValue(j, i) + dither, data.getValue(j, i) + '', undefined)
}
}
// Create and draw the visualization.
new google.visualization.LineChart(document.getElementById('visualization')).
draw(data, {width: 500, height: 400,
vAxis: {minValue: finalMin, maxValue: finalMax}}
);
}
Related
let input = [
[[1, 4], [40, 4]],
[[1, 5], [40, 5]],
[[4, 7], [4, 24]],
[[1, 9], [4, 1]],
[[1, 2], [6, 4]],
[[80, 4], [90, 4]],
[[4, 1], [4, 40]],
[[4, 35], [4, 29]],
[[4, 28], [4, 35]],
[[5, 3.6], [9, 5.2]],
]; // Input
Output = [
[[[1, 4], [40, 4]], [[80, 4], [90, 4]]],
[[[1, 5], [40, 5]]],
[[[4, 7], [4, 24]], [[4, 1], [4, 40]]],
[[[4, 35], [4, 29]], [[4, 28], [4, 35]]],
[[[1, 9], [4, 1]]],
[[[1, 2], [6, 4]], [[5, 3.6], [9, 5.2]]],
];
If given an input of series of each start and end coordinates of a line, for example, [[1,4],[40,4]] means that it has 2 points connecting [1,4] and [40,4] to form a straight line. My objective now is to group all those lines which share the same equation y=mx+c, together into a nested array as shown above. For example,
[[1,4],[40,4]] and [[80,4],[90,4]] share the same linear equation y=4
[[4,7],[4,24]],[[4,1],[4,40]] share the same linear equation x=4
[[1,2],[6,4]] and [[5,3.6],[9,5.2]] share the same linear equation y=0.4x+1.6
[[1,9],[4,1]] is alone and it has the linear equation of -2.67x+11.67
Here is my working codepen demo
I know how to code out to find those m and c in y=mx+c, but the problem is when for example,[[4,7],[4,24]] and [[4,1],[4,40]] , the m gradient becomes infinity which unsolvable.
Can anyone please guide me on this on how to get the correct output?
You can calculate the slope equation for each set of points and assign it to each array item, then group:
const input=[[[1,4],[40,4]],[[1,5],[40,5]],[[4,7],[4,24]],[[1,9],[4,1]],[[1,2],[6,4]],[[80,4],[90,4]],[[4,1],[4,40]],[[4,35],[4,29]],[[4,28],[4,35]],[[5,3.6],[9,5.2]]];
const inputsWithSlope = input.map((points) => {
const [[x, y], [x1, y1]] = points;
const slope = (y1 - y) / (x1 - x)
const b = y1 - slope * x1
return {
points,
line: x1 == x ? `x = ${x}` : `y = ${slope}x + ${b}`
}
})
const res = inputsWithSlope.reduce((acc, curr) => {
const accProp = acc[curr.line]
acc[curr.line] = !accProp ? [curr.points] : [...accProp, curr.points]
return acc
}, {})
const result = Object.values(res)
result.forEach(e => console.log(JSON.stringify(e)))
To deal with the rounding issue, you'll have to round it:
const input=[[[1,4],[40,4]],[[1,5],[40,5]],[[4,7],[4,24]],[[1,9],[4,1]],[[1,2],[6,4]],[[80,4],[90,4]],[[4,1],[4,40]],[[4,35],[4,29]],[[4,28],[4,35]],[[5,3.6],[9,5.2]]];
const inputsWithSlope = input.map((points) => {
const [[x, y], [x1, y1]] = points;
const slope = (y1 - y) / (x1 - x)
const b = y1 - slope * x1
return {
points,
line: x1 == x ? `x = ${x}` : `y = ${slope.toFixed(2)}x + ${b.toFixed(2)}`
}
})
const res = inputsWithSlope.reduce((acc, curr) => {
const accProp = acc[curr.line]
acc[curr.line] = !accProp ? [curr.points] : [...accProp, curr.points]
return acc
}, {})
const result = Object.values(res)
result.forEach(e => console.log(JSON.stringify(e)))
Here's my take at it. The strategy is to first get a description of the line for each pair of points, and then group those together into a map of maps, first keyed by slopes, and then by intercepts (either x or y, depending on if the line is vertical or not), and then extract the groups into a single array.
let input=[ [[1,4],[40,4]] , [[1,5],[40,5]] , [[4,7],[4,24]] ,[[1,9],[4,1]], [[1,2],[6,4]], [[80,4],[90,4]] , [[4,1],[4,40]] , [[4,35],[4,29]] , [[4,28],[4,35]] ,[[5,3.6],[9,5.2]] ] ;
// a function to get the slope and intercept of the line formed by a pair of points
function describeLine([[x1, y1], [x2, y2]]) {
if (x1 == x2) { // vertical line
return {m: "vertical", x: x1}
}
const p1 = x1 > x2 ? { x: x1, y: y1 } : { x: x2, y: y2 }
const p2 = x1 < x2 ? { x: x1, y: y1 } : { x: x2, y: y2 }
const m = (p1.y - p2.y) / (p1.x - p2.x)
const y = y1 - m * x1
return { m, y }
}
// this runs through the input array and accumulates all the pairs of points into a dictionary structure keyed by slope and then intercept
// so for a line with slope 2 and y-intercept 3, it would go into the map at { 2 : { 3: [[ [point 1], [point2]] ], [ line 2 with same props] ...
const maps = input.reduce((acc, line) => {
const desc = describeLine(line)
const m = acc[desc.m] || { }
if (desc.x) { // vertical line
x = m[desc.x] || []
return { ...acc, [desc.m]: { ...m, [desc.x]: [ ...x, line ]}}
} else {
y = m[desc.y] || []
return { ...acc, [desc.m]: { ...m, [desc.y]: [ ...y, line ]}}
}
}, {})
// once we've accumulated that structure, we can just collect the individual arrays into one big array
const sameLines = Object.values(maps).flatMap(Object.values)
console.log(JSON.stringify(sameLines, null, 2))
let's say I have a grid i.e. 2d array
const grid = [
[0, 0, A, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, B, 0],
[D, E, 0, C, F],
[0, 0, 0, 0, 0],
]
if some cell in the grid can visit all adjacent cells 4-diredtionally, for example, C is at [3, 3] so it can visit [3, 3 + 1], [3 - 1, 3],[3 +1, 3]``[3, 3 - 1], so normally I would have to hard code this like
// 👇 hard-coded directions
const dirs = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
]
const possibleMoves = []
for (const [dx, dy] of dirs) {
possibleMoves.push([dx + x, dy +y])
}
then if it can move 8-directionally then you have to hard code more directions
const dirs = [[1, 0], [-1, 0] , [0,1], [0,-1], [1,1], [-1,1], [-1,-1], [1,-1]]
Is there a smarter way to generate the dirs array for the next moves?
Yes!
First: any time you're doing grid-logic, start by checking what Amit Patel has to say.
Honestly, that link has everything you could ever need.
The short version is: if you know the grid width and cell layout, you can easily calculate coordinate offsets of any cell neighbor for any definition of "neighbor."
That logic can be implemented as a pure function that requires both the grid dimensions and the coordinates of the cell whose neighbors you want (aka the "target" cell):
let myNeighbors = getCellNeighbors(
{ x: 2, y: 2 }, // coords of target cell
{ width: 10, height: 10 } // grid dimensions, in cells
)
Or you can create a stateful thing that takes the grid dimensions at creation and calculates the offsets once, to be re-used for all getNeighbors calls:
let myGrid = new Grid(10, 10)
let myNeighbors = myGrid.getNeighbors(2, 5)
let myBiggerGrid = new Grid(25, 25)
let otherNeighbors = myBiggerGrid(2, 5)
I would like to fill until a specific Y value in PlotlyJS. This is as far as I got from the PlotlyJS docs: Fiddle
{
"x": [
"2016-01-31T00:03:57.000Z",
"2016-02-12T04:35:26.000Z"
],
"y": [
100,
100
],
"fill": "tonexty",
"fillcolor": "#8adcb3"
}
In the documentation, there seems to be two options:
tonexty - Fills as below. The problem is 'tonexty' is a bit limiting - The use case is 'filling until the line', so shading ONLY above 110. Example:
tozeroy - Fills till zero:
Also, do you need to introduce a new trace in order to create a fill?
This means that if I have a chart as follows (with only one trace but a threshold line as a shape): I need to introduce another trace just to create a fill. Maybe there's something I missed in the docs, or this is the wrong approach altogether.
So, how do you fill an area in a trace above a specific Y value in PlotlyJS?
A solution is to use multiple traces.
Split all your traces between ones which are above 0 and ones which are not.
When you are done you can fill them (or not) with the 'tozeroy' value.
The following jsfiddle shows a working example.
The code is as following :
HTML:
<div id="myDiv" style="width:600px;height:250px;"></div>
JS:
var data = [
{
x: ['A', 'B', 'C', 'D'],
y: [1, 3, 6, 0],
fill: 'tozeroy',
fillcolor: '#8adcb3'
},
{
x: ['D', 'F', 'G', 'I'],
y: [0, -3, -2, 0],
fill: 'toself'
},
{
x: ['I', 'J', 'K'],
y: [0, 5, 7],
fill: 'tozeroy',
fillcolor: '#0adcb3'
}
];
Plotly.newPlot('myDiv', data);
The result looks as following :
Here is another solution exploiting Plotly's fill: "toself". The idea is to create a closed line trace which encloses the area above the threshold and the markers of the main line. Works for threshold values above zero and for numerical x-values.
The helper traces have their legend hidden and are grouped with the main trace, thereby preventing ugly artifacts when toggling the legend.
The function checks for each x-y-pair if the y-value is above the threshold, if yes
check if there is already a segment above the threshold and use this one OR create a new sgement
the segement starts from the y-value of the threshold and the intermediate x-value from the point above the threshold and the one before.
each segment is terminated with an y-value which is equal to the threshol and the x-value which the mean of the last point in the segment and the next one
The function itself can be surely written in a nicer way but it's just a proof-of-concept.
function dataToTraces(data, threshold) {
var fillers = [];
var emptyFiller = {
x: [],
y: [],
fill: "toself",
mode: "lines",
line: {
width: 0
},
opacity: 0.5,
fillcolor: "#8adcb3",
showlegend: false,
legendgroup: "main"
}
fillers.push(emptyFiller);
for (var i = 0; i < data.y.length; i += 1) {
if (data.y[i] >= threshold) {
if (i !== 0 && data.y[i - 1] < threshold) {
fillers[fillers.length - 1].x.push(data.x[i - 1] + (threshold - data.y[i - 1]) / (data.y[i] - data.y[i - 1]));
fillers[fillers.length - 1].y.push(threshold);
}
fillers[fillers.length - 1].x.push(data.x[i]);
fillers[fillers.length - 1].y.push(data.y[i]);
} else if (fillers[fillers.length - 1].x.length > 0) {
if (i !== 0 && data.y[i - 1] !== threshold) {
fillers[fillers.length - 1].x.push(data.x[i - 1] + (threshold - data.y[i - 1]) / (data.y[i] - data.y[i - 1]));
fillers[fillers.length - 1].y.push(threshold);
}
fillers.push(emptyFiller);
}
}
return fillers;
}
var data = [{
x: [0, 1, 2, 3, 4, 5, 6, 7, 8],
y: [1, 3, 6, 2, -1, 5, 1, 3, 0],
name: "main",
legendgroup: "main"
}];
var fillers = dataToTraces(data[0], 2);
Plotly.newPlot("myDiv", data.concat(fillers));
<div id="myDiv"></div>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
Here you can see a chart created using graphael. http://jsfiddle.net/aNJxf/4/
It is shown with it's y axis correctly.
The first y value is 0.03100 and the maximum value at y axis is at 0.031
If we change the value to 0.03104 the maximum value at y axis becomes 1.03 and now all our points are in the bottom.
If we add another 0.00001, which makes that value 0.03105, the maximum at the axis y becomes 0.531 and now our points are shown at the wrong position of the chart.
It seems that something is going wrong while graphael calculates the maximum y axis value.
Why this happens? And how we can fix that?
The code that I have pasted there is
var r = Raphael("holder"),
txtattr = { font: "12px sans-serif" };
var x = [], y = [], y2 = [], y3 = [];
for (var i = 0; i < 1e6; i++) {
x[i] = i * 10;
y[i] = (y[i - 1] || 0) + (Math.random() * 7) - 3;
}
var demoX = [[1, 2, 3, 4, 5, 6, 7],[3.5, 4.5, 5.5, 6.5, 7, 8]];
var demoY = [[12, 32, 23, 15, 17, 27, 22], [10, 20, 30, 25, 15, 28]];
var xVals =[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58];
var yVals = [0.03100,0.02259,0.02623,0.01967,0.01967,0.00788,0.02217,0.0137,0.01237,0.01764,0.0131,0.00942,0.0076,0.01463,0.02882,0.02093,0.02502,0.01961,0.01551,0.02227,0.0164,0.0191,0.00774,0.03076,0.0281,0.01338,0.02763,0.02334,0.00557,0.00023,0.01523,0.0263,0.03077,0.02404,0.02492,0.01954,0.01954,0.02337,0.01715,0.02271,0.00815,0.01343,0.00985,0.01837,0.00749,0.02967,0.01156,0.0083,0.00209,0.01538,0.01348,0.01353];
//r.text(160, 10, "Symbols, axis and hover effect").attr(txtattr);
var lines = r.linechart(10, 10, 300, 220, xVals, yVals, { nostroke: false, axis: "0 0 1 1", symbol: "circle", smooth: true })
.hoverColumn(function () {
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{ fill: "#fff" }, { fill: this.symbols[i].attr("fill") }]));
}
}, function () {
this.tags && this.tags.remove();
});
lines.symbols.attr({ r: 3 });
Thanks
Sorry, I'm not real familiar with gRaphael, but I did find that converting your yVals into whole numbers (by multiplying each by 1e5) seemed to rid your chart of the awkward behavior.
This suggests that it could be related to the algorithm gRaphael uses to find the max axis value (as you ask in your related question) when your values are small decimal values (and alter at even more significant digits).
I know there are inherent issues with float precision, but I can't be sure that applies to your case, or that your values are low enough to consider this.
Not the best workaround, but if it would be feasible for you, you could display the yValues in an order of magnitude larger, and remind the viewer that they are actually smaller than presented. For example, your chart could go from 0 to 3100 and remind your viewer that the scale is scale * 1e-5.
(disclosure, I'm mostly math illiterate).
I have an array in this format:
var grid = [
[0,0], [0,1], [0,2], [0,3],
[1,0], [1,1], [1,2], [1,3],
[2,0], [2,1], [2,2], [2,3],
[3,0], [3,1], [3,2], [3,3]
];
I need to "rotate" it by 90deg increments, so it's like this:
var grid = [
[3,0], [2,0], [1,0], [0,0],
[3,1], [2,1], [1,1], [0,1],
[3,2], [2,2], [1,2], [0,2],
[3,3], [2,3], [1,3], [0,3]
];
How do I accomplish this in Javascript?
Rotating a two dimensional m x n matrix
Those looking for Rotating a two dimensional matrix (a more general case) here is how to do it.
example:
Original Matrix:
[
[1,2,3],
[4,5,6],
[7,8,9]
]
Rotated at 90 degrees:
[
[7,4,1]
[8,5,2]
[9,6,3]
]
This is done in following way:
matrix[0].map((val, index) => matrix.map(row => row[index]).reverse())
For counter-clockwise rotation (Thanks to #karn-ratana):
matrix[0].map((val, index) => matrix.map(row => row[row.length-1-index]));
Credit goes to this answer for the actual rotation method.
My method was pretty straightforward. Just determine what the row length was, and then iterate through each item, converting the array index to x/y equivalents and then apply the method used in the linked answer to rotate. Finally I converted the rotated X/Y coordinates back to an array index.
var grid = [
[0,0], [0,1], [0,2], [0,3],
[1,0], [1,1], [1,2], [1,3],
[2,0], [2,1], [2,2], [2,3],
[3,0], [3,1], [3,2], [3,3]
];
var newGrid = [];
var rowLength = Math.sqrt(grid.length);
newGrid.length = grid.length
for (var i = 0; i < grid.length; i++)
{
//convert to x/y
var x = i % rowLength;
var y = Math.floor(i / rowLength);
//find new x/y
var newX = rowLength - y - 1;
var newY = x;
//convert back to index
var newPosition = newY * rowLength + newX;
newGrid[newPosition] = grid[i];
}
for (var i = 0; i < newGrid.length; i++)
{
console.log(newGrid[i])
}
The output:
[3, 0] [2, 0] [1, 0] [0, 0]
[3, 1] [2, 1] [1, 1] [0, 1]
[3, 2] [2, 2] [1, 2] [0, 2]
[3, 3] [2, 3] [1, 3] [0, 3]
Fiddle for the lazy. And a 5x5 grid fiddle to demonstrate that the algorithm works for N grid sizes as long as they are square.
These are two function for clockwise and counterclockwise 90-degree rotation:
function rotateCounterClockwise(a){
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[j][n-i-1];
a[j][n-i-1]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[n-j-1][i];
a[n-j-1][i]=tmp;
}
}
return a;
}
function rotateClockwise(a) {
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[n-j-1][i];
a[n-j-1][i]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[j][n-i-1];
a[j][n-i-1]=tmp;
}
}
return a;
}
I don't really need to deal with indices, since I can copy the values from one place to the other, this simplifies the answer a bit:
var grid = [
[0,0], [0,1], [0,2], [0,3], [0,4],
[1,0], [1,1], [1,2], [1,3], [1,4],
[2,0], [2,1], [2,2], [2,3], [2,4],
[3,0], [3,1], [3,2], [3,3], [3,4],
[4,0], [4,1], [4,2], [4,3], [4,4]
];
var side = Math.sqrt(grid.length);
var rotate = function(d,i){
return [Math.abs(i % side - side+1), Math.floor(i/side)]
}
grid = grid.map(rotate);
You can see a jsfiddle here: http://jsfiddle.net/KmtPg/
This solution will work with any kind of matrix but if it's not nn and the size is nm it will have undefined items in the array (I set it to return null in case of undefined), but it works at the end and you will have a rotated array.
//ES6 function
const rotateMatrix = (matrix) => {
let temp;
for (let i = 1; i < matrix.length; i++) {
for (let j = 0; j < matrix.length / 2; j++) {
temp = matrix[i][j];
matrix[i][j] = matrix[j][i] === undefined ? null : matrix[j][i];
matrix[j][i] = temp;
}
}
return matrix;
};
// 3,3 matrix example
console.table(rotateMatrix([[1, 2, 64], [4, 5, 55], [7, 8, 34]]));
// 5*3 matrix example
console.table(rotateMatrix([[1, 2, 64], [4, 5, 55], [7, 8, 34], [10, 15, 65], [22, 24, 56]]));