I've got a simple problem, but I'm struggling to find the easiest solution without transforming the array a hundred times.
I want to do a simple stacked graph in google sheets, with weeks on X and values on Y. I got the values for each week, but only for weeks, that have a value.
The values are all calculations I've done with google apps script/ js.
person1 = [[2019/37,2], [2019/42,3]] and so on, for multiple persons and for 80 weeks in total.
The num value is the total value after each week. So I want the array to be filled up with the missing weeks. Therefore I mapped this to another array, where I have all the weeks but no values, giving these weeks the value 0:
person1= [[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
This of course does not fit to see a progress in the graph.
So I need something to set the weeks, which were filled up, to the previous value, resulting in
person1= [[2019/37,2],[2019/38,2],[2019/39,2],...,[2019/42,3],[2019/43,3],[2019/44,3],...]
Looping through this and setting the values with something like person[i][1] == person[i-1][1] seems not to be a good practice of course.
So, what would be the best way to achieve this? I'm kind of stuck with this now, I feel like I don't see the forest for the trees.
Thanks in advance!
code:
let valueArray = [[2019/37,2], [2019/42,3]]
let weeksArray = [2019/38,2019/39,2019/40,2019/41...]
//find missing weeks
let notFound = weeksArray.filter(el => valueArray.includes(el) == false).map(x => [x,0]);
//concat and sort
let outArray = arr.concat(notFound).sort((a,b)=> a[0].localeCompare(b[0]));
//output:
//[[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
Solution:
Since you already have the expanded array, you can use map on the whole array and use a function to replace the values:
var weeks = [[2019/37,2],[2019/38,0],[2019/39,0],[2019/40,3],[2019/41,0],[2019/42,4],[2019/43,0],[2019/44,0]];
weeks.map((a,b)=>{weeks[b][1] = (a[1] == 0 && b > 0) ? weeks[b-1][1] : weeks[b][1]});
To make it more readable, this is the same as:
weeks.forEach(function missing(item,index,arr) {
if (item[1] == 0 && index > 0) {
arr[index][1] = arr[index-1][1];
}
}
);
Console log:
References:
Arrow Functions
Conditional Operator
Array.prototype.map()
function fixArray() {
var array = [["2019/1", "1"], ["2019/10", "2"], ["2019/20", "3"], ["2019/30", "4"], ["2019/40", "5"]];
var oA = [];
array.forEach(function (r, i) {
oA.push(r);
let t1 = r[0].split('/');
let diff;
if (i + 1 < array.length) {
let inc = 1;
let t2 = array[i + 1][0].split('/');
if (t1[0] == t2[0] && t2[1] - t1[1] > 1) {
do {
let t3 = ['', ''];
t3[0] = t1[0] + '/' + Number(parseInt(t1[1]) + inc);
t3[1] = r[1];
diff = t2[1] - t1[1] - inc;
oA.push(t3);
inc++;
} while (diff > 1);
}
}
});
let end = "is near";
console.log(JSON.stringify(oA));
}
console.log:
[["2019/1","1"],["2019/2","1"],["2019/3","1"],["2019/4","1"],["2019/5","1"],["2019/6","1"],["2019/7","1"],["2019/8","1"],["2019/9","1"],["2019/10","2"],["2019/11","2"],["2019/12","2"],["2019/13","2"],["2019/14","2"],["2019/15","2"],["2019/16","2"],["2019/17","2"],["2019/18","2"],["2019/19","2"],["2019/20","3"],["2019/21","3"],["2019/22","3"],["2019/23","3"],["2019/24","3"],["2019/25","3"],["2019/26","3"],["2019/27","3"],["2019/28","3"],["2019/29","3"],["2019/30","4"],["2019/31","4"],["2019/32","4"],["2019/33","4"],["2019/34","4"],["2019/35","4"],["2019/36","4"],["2019/37","4"],["2019/38","4"],["2019/39","4"],["2019/40","5"]]
Highcharter (R wrapper for displaying Highcharts plots in R) does automatically adjust axis labels depending on the values, e.g. 405 will show as 405 whereas 3'000'000 will show as 3M.
I want to reproduce this for the tooltips shown.
This article outlines how to do this in Highcharts directly. However, I am not able to reproduce this using Highcharter.
Related:
highcharts tooltip format millions billions
JS Fiddle: http://jsfiddle.net/BlackLabel/ynCKW/104/
What am I missing?
chart_data <- tibble(
date_var = c(seq(from = as.Date('2019-09-10'), to = as.Date('2019-09-15'), by = 1))
, value = c(2304, 50000, 678900, 98457, 124684, 249547)
)
hchart(chart_data, type = 'line', hcaes(x = date_var, y = value)) %>%
hc_tooltip(formatter = JS(
"function() {
var axis = this.series.yAxis;
return axis.defaultLabelFormatter.call({
axis: axis,
value: this.y
});
}"
))
IMPORTANTLY: It seems to work for some values but not all of them. For instance, with above data I get correct tooltip labels for the 2nd and the 3rd date but all else show the original number.
In the related SO question you provided a link to, the assumption was to short numbers that have zeros (0) at the end. Just use the previous code from #Paweł Fus' answer:
formatter: function() {
var ret = '',
multi,
axis = this.series.yAxis,
numericSymbols = ['k', 'M', 'G', 'T', 'P', 'E'],
i = numericSymbols.length;
while (i-- && ret === '') {
multi = Math.pow(1000, i + 1);
if (axis.tickInterval >= multi && numericSymbols[i] !== null) {
ret = Highcharts.numberFormat(this.y / multi, -1) + numericSymbols[i];
}
}
return ret;
}
jsFiddle: https://jsfiddle.net/BlackLabel/yrb7gzap
Best regards!
I have an array like below...
var my_array = [
'00000000000000000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000000000000000000000',
]
... and I'd like to get a function that is able to calculate the bounds (4 corner positions) of the 1-characters in the array above.
So my result should be something like this:
var result= {
'top-left' : [6,1],
'bottom-left' : [6,5],
'top-right' : [13,1],
'bottom-right' : [13,5]
}
Also the function should be able to work on rotated arrays like the example array below:
var rotated_array = [
"00000000000000000000000",
"00000000001100000000000",
"00000000111110000000000",
"00000001111111000000000",
"00000111111111100000000",
"00000111111111000000000",
"00000011111100000000000",
"00000001110000000000000",
"00000000100000000000000",
"00000000000000000000000",
"00000000000000000000000"
]
// array ----> result should be something like this:
var result= {
'top-left' : [5,5],
'bottom-left' : [8,8],
'top-right' : [11,1],
'bottom-right' : [14,4]
}
I've developed a function that is able to return an array of the '1'-characters-positions (x/y-positions) so I will get an result array (from my_array) like this:
var positions = [[7,2],[7,1],[6,1],[6,2],[6,3],[6,4],[6,5],[7,5],[7,4],[7,3],[8,3],[8,2],[8,1],[9,1],[9,2],[9,3],[9,4],[9,5],[8,5],[8,4],[10,5],[10,4],[10,3],[10,2],[10,1],[11,1],[11,2],[11,3],[11,4],[11,5],[12,5],[12,4],[12,3],[12,2],[12,1],[13,1],[13,2],[13,3],[13,4],[13,5]]
Because the arraylist above includes all positions of my rectangle it also should includes the 4 corner positions... But how to get the right coordinates?
My idea was to filter the arrays biggest xValue, biggest yValue, smallest xValue and smallest yValue to get the 4 corners. Sound good, but there are also multiple possibilities for my corners:
var positions = [[7,2],[7,1],[6,1],[6,2],[6,3],[6,4],[6,5],[7,5],[7,4],[7,3],[8,3],[8,2],[8,1],[9,1],[9,2],[9,3],[9,4],[9,5],[8,5],[8,4],[10,5],[10,4],[10,3],[10,2],[10,1],[11,1],[11,2],[11,3],[11,4],[11,5],[12,5],[12,4],[12,3],[12,2],[12,1],[13,1],[13,2],[13,3],[13,4],[13,5]]
var corners = {
'maxX': positions.filter(e => e[0] === Math.max(...positions.map(e => e[0]))),
'minX': positions.filter(e => e[0] === Math.min(...positions.map(e => e[0]))),
'maxY': positions.filter(e => e[1] === Math.max(...positions.map(e => e[1]))),
'minY': positions.filter(e => e[1] === Math.min(...positions.map(e => e[1])))
}
console.log(JSON.stringify(corners))
So the code above returns something like this:
{
"maxX":[[13,1],[13,2],[13,3],[13,4],[13,5]], // x values are all 13 (max)
"minX":[[6,1],[6,2],[6,3],[6,4],[6,5]], // x values are all 6 (min)
"maxY":[[6,5],[7,5],[9,5],[8,5],[10,5],[11,5],[12,5],[13,5]], // y values are all 5 (max)
"minY":[[7,1],[6,1],[8,1],[9,1],[10,1],[11,1],[12,1],[13,1]] // y values are all 1 (min)
}
But how to get the right coordinates for each corner from the result above??
I am absolutely clueless how to calculate the right ones, so I hope somebody can help me with my function..
Thanks in advance, jonas
This is an extension of the comment on the question. Convert to vector and find the "pixel" closest to the corners.
If you can identify the "1" that borders the square (detect change from left to right and from top to bottom) and
Identify which "1" who belongs to which side of the square you can calculate the vector of each of the 4 borders using "minimum distance".
Afterwards you can calculate the intersection between the lines and you have the corners.
Minimum distance and intersection of lines are standard geometric operation, you can google. I cant remember them right know.
function findExtremes( array ){
let upRight = array[0],
downRight = array[0],
downLeft = array[0],
upLeft = array[0];
for(const [x,y] of array){
if(x < downRight[0] || x === downRight[0] && y < downRight[1]) downRight = [x,y];
if(x > upRight[0] || x === upRight[0] && y > upRight[1]) upRight = [x,y];
if(y > upLeft[1] || y === upLeft[1] && x < upLeft[0]) upLeft = [x,y];
if(y < downLeft[1] || y === downLeft[1] && x > downLeft[0]) downLeft = [x,y];
}
return {downRight, downLeft, upRight, upLeft};
}
The code works like this:
x < downRight[0]
This first part insures that we take the smallest x value. OR if
x === downRight[0]
weve got two minimal x values, we take
y < downRight[1]
the one with the smaller y.