I'm trying to write a function that returns an array of equally chunked up dates and number of days pertaining to those dates. Should there be a remainder of those days they get appended to the array as follow.
Expected outcome:
[{
'startDate' : 20160719 //dates generated from momentjs
'numOfDays': 5
},{
'startDate' : 20160724
'numOfDays': 5
},{
'startDate' : 20160729
'numOfDays': 3
}]
Below is the function I've written in which you can pass in a start date (momentjs), the total number of days (daysToDisplay) and number of days to be divided by (numOfDays).
function buildQueue(startDate, numOfDays, daysToDisplay) {
if (!startDate || !numOfDays || !daysToDisplay) {
throw new Error('All params required!');
}
var num = numOfDays > daysToDisplay ? daysToDisplay : numOfDays;
var div = Math.floor(daysToDisplay / num);
var count = daysToDisplay;
var rem = daysToDisplay % num;
var lastItem;
var i;
var arr = [];
for (i = 0; i <= daysToDisplay; i += num) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD').add(i, 'days').format('YYYYMMDD'),
numOfDays: numOfDays,
count: i
})
if (rem === count) {
break;
}
count -= num;
}
if (count > 0) {
lastItem = arr[arr.length - 1];
var leftover = daysToDisplay - lastItem.count;
arr.push({
startDate: moment(lastItem.startDate, 'YYYYMMDD').add(num, 'days').format('YYYYMMDD'),
numOfDays: rem,
count: leftover + lastItem.count
});
}
return arr;
}
A working example is here (https://jsfiddle.net/zv5ghqpa/1/). The code appears to work in scenarios where daysToDisplay is dividable by more than 2.
When daysToDisplay is only dividable by one, we get an additional item in the returned array basically due to the zero index in the for loop. The expected outcome if I call buildQueue('20160719', 5, 8) should be:
[{
'startDate': 20160719
'numOfDays': 5
}, {
'startDate': 20160724
'numOfDays': 3
}]
Instead its returning:
[{
'startDate': 20160719
'numOfDays': 5
},{
'startDate': 20160724
'numOfDays': 5
}, {
'startDate': 20160729
'numOfDays': 3
}]
I hope i've given enough info... this is really doing my head in.
Thanks in advance!
I think this is code you're looking for:
function buildQueue(startDate, numOfDays, daysToDisplay) {
if (!startDate || !numOfDays || !daysToDisplay) {
throw new Error('All params required!');
}
var num = numOfDays > daysToDisplay ? daysToDisplay : numOfDays;
var div = Math.floor(daysToDisplay / num);
var count = daysToDisplay;
var rem = daysToDisplay % num;
var n = 0;
var i;
var arr = [];
for (i = 0; i <= daysToDisplay; i += num) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD').add(i, 'days').format('YYYYMMDD'),
numOfDays: daysToDisplay % num,
count: i
})
console.log(rem + ' ' + count);
if (rem === count) {
count = 0;
break;
}
count -= num;
}
if (count > 0) {
var leftover = daysToDisplay - arr[arr.length - 1].count;
arr.push({
startDate: moment(arr[arr.length - 1].startDate, 'YYYYMMDD').add(num, 'days').format('YYYYMMDD'),
numOfDays: daysToDisplay % num,
count: leftover + arr[arr.length - 1].count
});
}
return arr;
}
//console.log(buildQueue(moment(), 80, 100));
console.log(buildQueue(moment(), 5, 8));
//console.log(buildQueue(moment(), 15, 100));
//console.log(buildQueue(moment(), 30, 100));
//console.log(buildQueue(moment(), 45, 100));
I think the 'Expected outcome' was correct before you edited the question. I also note the sample code produced a count property that you don't want in the output.
Perhaps this code does what you want?
function buildQueue(startDate, numOfDays, daysToDisplay) {
var arr = []
while (daysToDisplay > 0) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD')
.add(numOfDays * arr.length, 'days')
.format('YYYYMMDD'),
numDays: Math.min(numOfDays, daysToDisplay)
})
daysToDisplay -= numOfDays
}
return arr
}
Related
I want to convert the ranges of below fashion to rows and column notations
The example input and the expected output is given below
Input is A1 => Then output is [0,1,0,1]
B1 = [0,1,1,2]
A2 = [1,2,0,1]
C2 = [1,2,2,3]
C12 = [11,12,2,3]
A1:B4 = [0,4,0,2]
C1:D11 = [0,11,2,4]
F32:H43 = [31,43,5,8]
C:F = [null,null,2,6]
30:38 = [29,38,null,null]
76:79 = [75,79,null,null]
AA1:AD15 = [0,15,26,30]
Z2:B1 = [null,null,null,null] or undefined //any invalid range
2:B1 = [null,null,null,null] or undefined //any invalid range
0:0 = [null,null,null,null] or undefined //any invalid range
4-2 = [null,null,null,null] or undefined //any invalid range
6-7d7 = [null,null,null,null] or undefined //any invalid range
The general formula
[startrow-1,endrow,startcol-1,endcol]
30:38 = [startrow-1,endrow,null,null]
76:79 = [startrow-1,endrow,null,null]
C:F = [null,null,2,6]
A1
can also be written as
A1:A1 = [0,1,0,1]
C12
can also be written as
C12:C12 = [11,12,2,3]
My code works for these
A10:B10 //already works
A:B10 //not working so far
A10:B //not working so far
A:B //not working so far
10:10 //not working so far
<!DOCTYPE html>
<html>
<body>
<script>
function fromA1Notation(cell) {
var i, l, chr,
sum = 0,
A = "A".charCodeAt(0),
radix = "Z".charCodeAt(0) - A + 1;
if (typeof cell !== 'string' || !/^[A-Z]+$/.test(cell)) {
throw new Error("Expected column label");
}
for (i = 0, l = cell.length; i < l; i++) {
chr = cell.charCodeAt(i);
sum = sum * radix + chr - A + 1
}
return sum;
}
var input = "A1:B20";
if (input.length > 0 && input.match(/[A-Z]+[0-9]+:[A-Z]+[0-9]+/i) != null) {
var matched = input.match("([A-Za-z]+)([0-9]+):([A-Za-z]+)([0-9]+)");
console.log(JSON.stringify(matched))
if (matched != null) {
a1range = {
a1not: input,
c1: (fromA1Notation(matched[1].toUpperCase()) - 1),
r1: (matched[2] - 1),
c2: fromA1Notation(matched[3].toUpperCase()),
r2: matched[4]
};
if (a1range.c1 >= a1range.c2 || a1range.c1 >= a1range.c2) {
a1range = undefined;
}
console.log(a1range)
}
}
console.log(a1range)
</script>
</body>
</html>
Here is a solution that:
turns 'A1' pattern into 'A1:A1'
matches col, num, ':', col, num
converts col letters into 1-based index
does invalid range check
fixed the start row and start col index to be zero based
function A2N (str) {
return str.split('').reduce((acc, char, idx) => {
return acc += char.charCodeAt(0) - 65 + (idx * 26);
}, 1);
}
[ 'A1', 'B1', 'A2', 'C:F', 'A10:B10', 'A:B10', 'A10:B', 'A:B', '10:10', 'AA1', 'AAA1', 'A', '1', 'B', '20', 'Z9:A1', '#'
].forEach(str => {
let parts = str
.replace(/^(\w+)$/, '$1:$1') // turn 'A1' into 'A1:A1'
.match(/^([A-Z]*)([0-9]*)(?::([A-Z]*)([0-9]*))?$/);
let result = [ null, null, null, null ];
if(parts) {
result = [
parts[2] ? Number(parts[2]) : null,
parts[4] ? Number(parts[4]) : null,
parts[1] ? A2N(parts[1]) : null,
parts[3] ? A2N(parts[3]) : null
];
if(result[0] && result[1] && result[0] > result[1]) {
// invalid range
result[0] = null;
result[1] = null;
}
if(result[2] && result[3] && result[2] > result[3]) {
// invalid range
result[2] = null;
result[3] = null;
}
if(result[0]) {
// zero-based start row
result[0]--;
}
if(result[2]) {
// zero-based start col
result[2]--;
}
}
console.log(str, '==>', result);
});
I'm trying to write a script to output JSON according to these constraints.
So far I think my logic is correct.
Any help is appreciated.
CURRENT ISSUES:
[working now]I can't figure out why duration continues to return 0
[working now]how to tackle setting the max/min
tackling how to handle when two excursions of different types occur back to back (“hot” ⇒ “cold” or “cold” ⇒ “hot”)
This is how each new object should appear
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
device_sensor
The sId this excursion was detected on.
start_at
The date and time the temperature first is out of range in ISO_8601 format.
stop_at
The date and time the temperature is back in range in ISO_8601 format.
duration
The total time in seconds the temperature was out of range.
type
Either the string “hot” or “cold” depending on the type of excursion.
max/min
The temperature extreme for the excursion. For a “hot” excursion this will be the max and for a “cold” excursion the min.
A temperature excursion event starts
when the temperature goes out of range and ends when the temperature returns
to the range.
For a “hot” excursion this is when the temperature is greater than 8 °C,
and for a “cold” excursion this is when the temperature is less than 2 °C.
If two excursions of different types occur back to back
(“hot” ⇒ “cold” or “cold” ⇒ “hot”) please take the midpoint of the two
timestamps as the end of the first excursion and the start of the second.
If an excursion is occurring at the end of the temperature readings
please end the excursion at the last reading (duration = 0)
Here is the link to the test data
Test Case Data
Here is what I've written so far:
const tempTypeTernary = (num) =>{
if(num < 2){
return 'cold'
} else if(num > 8){
return 'hot'
}
}
const excursion_duration = (x,y) =>{
let start = new Date(x) / 1000
let end = new Date(y) / 1000
return end - start
}
const reset_excursion = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = ''
})
}
const list_excursion = (array) =>{
let result = [];
let max_min_excursion = 0;
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
for(let k = 0; k < array.length;k++){
if( array[k]['tmp'] < 2 || array[k]['tmp'] > 8){
current_excursion['device_sensor'] = array[k]['sId'];
current_excursion['start_at'] = [new Date(array[k]['time']).toISOString(),array[k]['time']];
current_excursion['type'] = tempTypeTernary(array[k]['tmp']);
if( array[k]['tmp'] > 2 || array[k]['tmp'] < 8){
current_excursion['stop_at'] = new Date(array[k]['time']).toISOString();
current_excursion['duration'] = excursion_duration(current_excursion['start_at'][1],array[k]['time'])
}
result.push(current_excursion)
reset_excursion(current_excursion)
}
}
return result
}
list_excursion(json)
Let me be bold and try to answer on just eyeballing the code; please tryout this:
const tempTypeTernary = (num) =>{
if(num < 2){
return 'cold'
} else if(num > 8){
return 'hot'
}
}
const excursion_duration = (x,y) =>{
let start = new Date(x) / 1000
let end = new Date(y) / 1000
return end - start
}
const reset_excursion = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = ''
})
}
const list_excursion = (array) =>{
let result = [];
let max_min_excursion = 0;
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
for(let k = 0; k < array.length;k++){
if( array[k]['tmp'] < 2 || array[k]['tmp'] > 8)
{
if (current_excursion['type']==null)
{
current_excursion['device_sensor'] = array[k]['sId'];
current_excursion['start_at'] = [new Date(array[k]['time']).toISOString(),array[k]['time']];
current_excursion['type'] = tempTypeTernary(array[k]['tmp'])
}
}
else // this is where the second 'if' was
{
if (current_excursion['type']!=null)
{
current_excursion['stop_at'] = new Date(array[k]['time']).toISOString();
current_excursion['duration'] = excursion_duration(current_excursion['start_at'][1],array[k]['time'])
result.push(current_excursion)
reset_excursion(current_excursion)
}
}
}
This question already has answers here:
How to format numbers as currency strings
(67 answers)
Closed 5 years ago.
I need convert strings to price format.
For example
150 = 150.00
1000 = 1'000.00
25500 = 25'500.00
1000.80 = 1'000.80 etc.
I wrote code but not sure it is good:
function insert(str, index, value) {
return str.substr(0, index) + value + str.substr(index);
}
function convert(n) {
n = n.toString();
var length = n.length;
if (length < 4) {
n = insert(n, length, '.00');
} else if (length === 4) {
n = insert(n, 1, "'");
n = insert(n, length + 1, '.00');
} else if (length > 4) {
var floatFlag = false;
if (n.indexOf('.') > -1 || n.indexOf(',') > -1) {
floatFlag = true;
n = n.replace(/,/g, '.');
}
var thouthandNumer = n / 1000;
thouthandNumer = thouthandNumer | 0;
n = n.replace(thouthandNumer, thouthandNumer + "'");
if (!floatFlag) {
n = insert(n, length + 1, '.00');
}
}
}
var n = 15000
convert(n); //return 15'000.00
How can I convert strings in correct way? Thanks for help.
Use Number.toLocaleString():
let x = 25500;
console.log(x.toLocaleString('en-us', {minimumFractionDigits: 2}));
i was wondering if there is a way to get an array with minimum and maximum values get by another array, or something like this:
var numbers = ['10','15','20','25','30','35','40','45','50'];
var getnumbers = function(min,max){
//return a result that is push to another array, in this case "results"
};
getnumbers(10,30);
console.log(results);
the output should give me something like 10 - 15 - 20 - 25 - 30
A slightly different approach, with a customized callback function for filtering.
function filterNumbers(min, max) {
return function (a) { return a >= min && a <= max; };
}
var numbers = ['10', '15', '20', '25', '30', '35', '40', '45', '50'],
result = numbers.filter(filterNumbers(10, 30));
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
You can do something like: numbers.filter(function (value) { return value >= 10 && value <= 30 });
And if you really want the output to be N1 - N2 - N3 ... - NN, just do numbers.filter(function (value) { return value >= 10 && value <= 30 }).join(' - ');
try this
var numbers = ['10','15','20','25','30','35','40','45','50'];
var output = "";
var getnumbers = function(min,max){
for (var i = 0; i < numbers.length; i++){
if(numbers[i] >= min && numbers[i] <= max){
output += numbers[i] + "-";
}
}
};
getnumbers(10,30);
console.log(output);
JavaScript has inbuilt functions to solve this issue.
let numbers = ['10','15','20','25','30','35','40','45','50'];
// Get all elements between '10' and '30'
let result = numbers.splice(numbers.indexOf('10'),numbers.indexOf('30') + 1);
console.log(result);
// If you want the answer to literally be '10-15-20-25-30'
let resultString = "";
for(const i in result){
resultString.concat(`${result[i]}-`)
}
console.log(resultString);
Lets say you have input Array=[1,2,3,5,7,9,10,11,12,15]
The output should be 1-3,5,7,9-12,15
Im looking for feedback on my attempt and other possible solutions.
Heres my attempt in javascript:
var min = 0;
var max = -1;
function summarize(array) {
var sumString = "";
var prevVal = -1;
array.forEach(function(currVal, index) {
if (index > 0) {
prevVal = array[index - 1];
}
if (index === 0) {
min = currVal;
max = currVal;
} else if (currVal - prevVal === 1) {
max = currVal;
} else if (min !== max && max !== -1) {
sumString += min + "-" + max + (index < array.length - 1 ? "," : "");
min = currVal;
max = -1;
} else {
sumString += min + (index < array.length - 1 ? "," : "");
}
if (index === array.length - 1) {
if (max === -1) {
sumString += "," + min;
} else {
sumString += min + "-" + max;
}
}
});
return sumString;
}
Here is a slightly shorter implementation:
var i = 0, prev, arr = [1,2,3,5,7,9,10,11,12,15], out = [];
for(i=0; i<arr.length; prev = arr[i], i++) {
// if the current number is not prev+1, append it to out
// Note that we are adding it as a string, to ensure that
// subsequent calls to `split()` (see else part) works
if(prev !== arr[i] - 1) out.push(String(arr[i]));
// if the current number is prev+1, modify the last value
// in out to reflect it in the RHS of - (hyphen)
else out[out.length - 1] = [out[out.length - 1].split('-')[0], String(arr[i])].join('-');
}
// out => ["1-3", "5", "7", "9-12", "15"]
Another possible solution for positive numbers in ascending order. It features Array.prototype.reduce.
var array = [1, 2, 3, 5, 7, 9, 10, 11, 12, 15, 23, 24],
result = [];
array.reduce(function (r, a) {
result.push(r + 1 - a ? String(a) : result.pop().split('-')[0] + '-' + String(a));
return a;
}, array[0]);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Another possible solution :
var points = [1,2,3,5,6,31,7,9,10,11,12,15];
points.sort(function(a, b){return a-b}); //sort array in asc
var resultArr=[];
var max; var min;
for(i=0;i<points.length;i++) //loop
{
if(i==0)
{
min=points[i]; //lowest number in arr
max=points[i]+1; //assign next value
}
else
{
if(max==points[i]) //if value matches continue
max=points[i]+1;
else //next value is not an incremental one so push it to result arr
{
max=max-1;
resultArr.push(min+(min!=max? "-"+ max :""));
min=points[i];
max=points[i]+1;
}
if(i==points.length-1) //last element of the arr so push it to result arr
{
max=max-1;
resultArr.push(min+(min!=max? "-"+ max :""));
}
}
}
alert(resultArr);
First step uses dashes to separate sequential numbers and commas if they aren't. Second step replaces -#- with -.
var X = [1,2,3,5,7,9,10,11,12,15];
var S = '' + X[0];
for (var i = 1; i < X.length; i++) {
S += (X[i] == X[i - 1] + 1)? '-': ',';
S += X[i];
}
while (/-[0-9]+-/.test(S))
S = S.replace(/-[0-9]+-/g, '-');
alert(S);
For a sequence like 1,2,5,6 will output 1-2,5-6 which might not be what you're looking for, so an optional third step would be to replace #-#+1 with #,#+1, i.e. restore the comma:
for (var i = 1; i < X.length; i++)
S = S.replace(X[i - 1] + '-' + X[i], X[i - 1] + ',' + X[i]);
I ran into this problem recently, after some reflection, I noticed 3 different transformations: (1) Group consecutive numbers; (2) Transform groups into strings representing the ranges; (3) Join range strings on comma.
function summarizeRange(items) {
const sorted = items.slice(0).sort((a, b) => a - b);
return sorted
.slice(1)
.reduce((range, item) => {
const rangedIndex = range.reduce((ranged, rangedCollection, index) =>
rangedCollection.indexOf(item - 1) > -1 ? index : ranged,
-1
);
if (rangedIndex > -1) {
range[rangedIndex] = range[rangedIndex].concat(item);
return range;
}
return range.concat([
[item]
]);
}, [
[sorted[0]]
])
.map(range => range.length > 1 ?
'' + range[0] + '-' + range[range.length - 1] :
'' + range[0]
)
.join(',');
}
console.log(summarizeRange([0,3,2,6,19,20,22,21,1]));