I'm looking if there is an alternative to write this code cleaner and efficient.
My goal is to set different values based on the hour of the day.
var hour = new Date().getHours();
var user = 'User4';
if (hour >= 4 && hour < 6) {
user = 'User1';
}
if (hour >= 6 && hour < 13) {
user = 'User2';
}
if (hour >= 13 && hour < 19) {
user = 'User3';
}
if (hour >= 19) {
user = 'User4';
}
I found this article online, but I couldn't achieve logical operators into this.
let values = {
a: 1,
b: 2,
};
let foo = values[ bar ] || 3;
Really appreciate your help! Thanks 😗
You can use else if to make code shorter and cleaner
var hour = new Date().getHours();
var user;
if (hour < 4 || hour >= 19) {
user = 'User4';
} else if (hour < 6) {
user = 'User1';
} else if (hour < 13) {
user = 'User2';
} else if (hour < 19) {
user = 'User3';
}
console.log(user)
Using switch(true)
var hour = new Date().getHours();
var user;
switch(true) {
case (hour < 4 ||
hour >= 19): user = 'User4'; break;
case (hour < 6) : user = 'User1'; break;
case (hour < 13) : user = 'User2'; break;
case (hour < 19) : user = 'User3'; break;
}
console.log(user)
What about using conditional operators
var hour = new Date().getHours();
var user = 'User4'
user=(hour >= 4 && hour < 6)?'User1': (hour >= 6 && hour < 13)?'User2':(hour >= 13 && hour < 19)?'User3':(hour >= 19)?'User4':''
console.log(user)
If you mean you want to do it by ternary operators, you can try
let user = hour <13? (hour >= 6 && hour < 13)? "User 2" : "User 1" : (hour >= 13 && hour < 19)? "User 3" : "User 4"
This should be the simplest way to achieve the desired result, however you may continue with your own example and do something like
let values = {
a: "User 1",
b: "User 2",
c: "User 3",
d: "User 4"
};
let bar = hour <13? (hour >= 6 && hour < 13)? "b" : "a" : (hour >= 13 && hour < 19)? "c" : "d"
let foo = values[ bar ] || 3;
If you want to write this as an object, you need to list all 24 hours, making the code simpler, but longer:
var hour = new Date().getHours();
var name = 'User';
var id = {
0: 4,
1: 4,
2: 4,
3: 4,
4: 1,
5: 1,
6: 2,
7: 2,
8: 2,
9: 2,
10: 2,
11: 2,
12: 2,
13: 3,
14: 3,
15: 3,
16: 3,
17: 3,
18: 3,
19: 4,
20: 4,
21: 4,
22: 4,
23: 4
};
var user = name + id[ hour];
console.log(user);
As mentioned in the article you linked, I feel this is one of those logic blocks where ternaries make things ugly.
So personally i would prefer the if/else version below since its the most readable as if it were 'plain english'.
Here is an alternative that doesn't use if or switch:
var periods = [
{ from: 0, to: 4, user: 'User4' },
{ from: 4, to: 6, user: 'User1' },
{ from: 6, to: 13, user: 'User2' },
{ from: 13, to: 19, user: 'User3' },
{ from: 19, to: 23, user: 'User4' }
];
var hour = new Date().getHours();
var user = periods.find(p => hour >= p.from && hour < p.to).user;
You could move the conditions inside of an own function for getting the user and return early if the condition match.
function getUser(hour) {
if (hour < 4) return 'User4';
if (hour < 6) return 'User1';
if (hour < 13) return 'User2';
if (hour < 19) return 'User3';
return 'User4';
}
var hour = new Date().getHours(),
user = getUser(hour);
console.log(user);
I don't get what #Asaf is asking but if he want something like below one
let values = {
a: 1,
b: 2,
};
let foo = values[ bar ] || 3;
he can try something like this (This is not prefered way to do)
var hour = new Date().getHours();
let values = {
4 : 'User1',
5 : 'User1',
6 : 'User2',
7 : 'User2',
8 : 'User2',
9 : 'User2',
10 : 'User2',
11 : 'User2',
12 : 'User2',
13 : 'User3',
14 : 'User3',
15 : 'User3',
16 : 'User3',
17 : 'User3',
18 : 'User3',
};
let foo = values[ hour ] || 'User4';
console.log(foo);
From a Code golf point of view, chaining the ternary operator could be the shortest method.
But it doesn't help with the readability if it's used in one long line.
But a bit of code indentation helps.
var hour = new Date().getHours();
var user = (hour >= 4 && hour <= 5 ? "User1"
: hour >= 6 && hour <= 12 ? "User2"
: hour >= 13 && hour <= 18 ? "User3"
: hour >= 19 ? "User4"
: "User4");
console.log(hour, user);
Or this shorter version:
var hour = new Date().getHours();
var user = (hour < 4 || hour > 18 ? "User4"
: hour < 6 ? "User1"
: hour < 13 ? "User2"
: "User3");
console.log(hour, user);
Your current approach is fine and is efficient. An alternative approach you could take is a functional one, where you define each range using a function like so:
const hour = new Date().getHours();
const I = x => x;
const K = x => y => x;
const setRange = (x, y, out) => (f, h) => f(h >= x && h < y)(out);
const createRanges = (...ranges) => def => h => {
for(const rf of ranges)
if(rf(K, h)) return rf(K(I), h);
return def;
}
const getUser = createRanges(
setRange(4, 6, 'User1'),
setRange(6, 13, 'User2'),
setRange(13, 19, 'User3')
)("User4"); // default/else "User 4"
const user = getUser(hour);
console.log(hour, user);
Related
I started learning JavaScript and as a part of a small project, I have an array of Date objects, and I am trying to search in it with binary search. I don't know why, but it became an infinite loop.
I know the problem is in the first if (if mid < right), because it works when the content of the if stands alone.
But if I put only the content, the function gets stuck when mid = right.
I don't see what in the if makes it an infinite loop or how to fix it.
I would greatly appreciate it if you could be of assistance.
**timeDiff is a function that returns which of the two dates are greater (later)
EDIT
I will try to describe the code better...
the function of the binary search receives an array of date objects (db = database), and one more date object (objDate), that the search should find.
I want the search function to return if the searched object is in the array (this is the true and false), and what is the index of it (or the index it should be in, if it is not on the array) - this is why I return also the mid.
I know it is not the best thing to return two values, but I need them two, so I put them in two variables when I call the function.
I add here the function timeDiff (that receives two Date objects and returns which of them is greater (later)) and an example for running.
function timeDiff(objDate, currDate) {
var _MS_PER_DAY = 86400000
var day1 = Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate(), objDate.getHours(), objDate.getMinutes());
var day2 = Date.UTC(currDate.getFullYear(), currDate.getMonth(), currDate.getDate(), currDate.getHours(), currDate.getMinutes());
return (day1 - day2) / _MS_PER_DAY ;
function binarySearch(db, objDate){
var left = 0, right = db.length ;
while (left <= right){
var mid = Math.trunc((left + right) / 2) ;
if (mid != right) {
var currDate = new Date(db[mid].date) ;
}
if (timeDiff(objDate, currDate) === 0){
return true, mid ;
}
if (timeDiff(objDate, currDate) < 0){
right = mid - 1 ;
}
if (timeDiff(objDate, currDate) > 0){
left = mid + 1 ;
}
}
(timeDiff(objDate, currDate) > 0) ? mid-- : mid = mid ;
return false, mid ;
}
a running example that should return "true, 0" (but actually does not finish the run) is:
var a = [{ "date": "2018-09-12", "appointments": [ { "subject": "Code review", "start": "09:00", "duration": 1.5 }, { "subject": "JavaScript objects", "start": "10:45", "duration": 2 } ] }] ;
var b = new Date(2018, 09, 12, 8, 0)
binarySearch(a, b)
It does end, it only loops twice.
function timeDiff(objDate, currDate) {
var _MS_PER_DAY = 86400000
var day1 = Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate(), objDate.getHours(), objDate.getMinutes());
var day2 = Date.UTC(currDate.getFullYear(), currDate.getMonth(), currDate.getDate(), currDate.getHours(), currDate.getMinutes());
return (day1 - day2) / _MS_PER_DAY ;
}
function binarySearch(db, objDate){
var left = 0, right = db.length ;
while (left <= right){
var mid = Math.trunc((left + right) / 2) ;
if (mid != right) {
var currDate = new Date(db[mid].date) ;
}
if (timeDiff(objDate, currDate) === 0){
return true, mid ;
}
if (timeDiff(objDate, currDate) < 0){
right = mid - 1 ;
}
if (timeDiff(objDate, currDate) > 0){
left = mid + 1 ;
}
console.log('looping');
}
(timeDiff(objDate, currDate) > 0) ? mid-- : mid = mid ;
return false, mid ;
}
var a = [{ "date": "2018-09-12", "appointments": [ { "subject": "Code review", "start": "09:00", "duration": 1.5 }, { "subject": "JavaScript objects", "start": "10:45", "duration": 2 } ] }] ;
var b = new Date(2018, 09, 12, 8, 0)
binarySearch(a, b)
You could use the find method of array
var a = [{ "date": "2018-09-12", "appointments": [ { "subject": "Code review", "start": "09:00", "duration": 1.5 }, { "subject": "JavaScript objects", "start": "10:45", "duration": 2 } ] }] ;
var b = new Date(2018, 09, 12, 8, 0)
console.log(a.find(item => item.date === `${b.getFullYear()}-${pad(b.getMonth())}-${pad(b.getDate())}`));
function pad(num) {
return num < 10 ? '0' + num : num;
}
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
}
I am trying to check the week object's minutes and hours and I cannot figure out what I am doing wrong. The week object can contain variations of Day1 - Day7 so I dont want to check them specifically. I want to check the nested Hours/Minutes. I also don't want to use jquery and it has to work with ie8. Any help would be greatly appreciated.
week = {
Day1: {
Hours: 6,
Minutes: 20
},
Day2: {
Minutes: 45
},
Day3: {
Hours: 8,
Minutes: 15
}
};
hoursInValid = false;
minutesInValid = false;
for (var item in week) {
if (week.hasOwnProperty(item)) {
for (var i = 0; i < week[item].length; i++ )
{
if (week[item][i].Hours > 6) {
hoursInValid = true;
break;
}
if (week[item][i].Minutes > 20) {
minutesInValid = true;
break;
}
}
}
}
I don't see the need for the internal for loop. This is the solution I came up with. I added checks to make sure the DayN objects have Hours and Minutes properties.
week = {
Day1: {
Hours: 6,
Minutes: 20
},
Day2: {
Minutes: 45
},
Day3: {
Hours: 8,
Minutes: 15
}
};
hoursInValid = false;
minutesInValid = false;
for (var item in week) {
if (week[item].hasOwnProperty('Hours')) {
if (week[item].Hours > 6) {
hoursInValid = true;
break;
}
}
if (week[item].hasOwnProperty('Minutes')) {
if (week[item].Minutes > 20) {
minutesInValid = true;
break;
}
}
}
Try this:
for (var day in week) {
for (var unit in week[day]) {
if (unit === 'Hours' && week[day][unit] > 6) {
hoursInvalid = true;
} else if (unit === 'Minutes' && week[day][unit] > 20) {
minutesInvalid = true;
}
}
}
The break statements may not allow you to iterate over all the properties.
Do this instead:
var invalidHours = {}, invalidMinutes = {};
for(var i in week){
var w = week[i];
if(w.hasOwnProperty('Hours')){
invalidHours[i] = w.Hours > 6 ? true : false;
}
else{
// no hours
}
if(w.hasOwnProperty('Minutes')){
invalidMinutes[i] = w.Minutes > 20 ? true : false;
}
else{
// no minutes
}
}
if(invalidHours.Day1) // day 1 hours are invalid
if(invalidMinutes.Day2) // day 2 minutes are invalid
First I have an array, like this:
var arr = [[2,3,4,5,6,7,8,9,10],
[3,4,5,6,7,8,9,10,11],
[4,5,6,7,8,9,10,11,12]]
It is not necessarily should be this array, it could be any other, it is just example. So I need to know the count of all possible permutations, something like this:
2,3,4
2,3,5
...
2,3,12
2,4,5
2,4,6
...
2,4,12
...
10,11,12
It is not simple permutations, as I understand. And also I don't need all this combination, I just need their count, may be exist formula or something like this. In this example I know that answer is 165. Thank you
Assuming that the order must increase:
var arr = [[2,3,4,5,6,7,8,9,10],
[3,4,5,6,7,8,9,10,11],
[4,5,6,7,8,9,10,11,12]];
function count(stack, history) {
var counter = 0;
history = history || [];
stack[0].forEach(function(it) {
if (! history.length || history[history.length -1] < it) {
if (stack[1]) {
counter += count(stack.slice(1), history.concat([it]));
} else {
counter++;
}
}
});
return counter;
}
console.log(count(arr)); //165
This is an interesting mathematical problem. I think what you are referring to is combinations and not permutations. I place the javascript here first. The math follows below that.
<script type="text/javascript">
var arr = new Array();
arr[0] = new Array(2,3,4,5,6,7,8,9,10);
arr[1] = new Array(3,4,5,6,7,8,9,10,11);
arr[2] = new Array(4,5,6,7,8,9,10,11,12);
function countcombinations() {
var history = new Array();
for (var i=0;i<arr[0].length;i++) {
for (var j=0;j<arr[1].length;j++) {
for (var k=0;k<arr[2].length;k++) {
if (arr[0][i] == arr[1][j] || arr[0][i] == arr[2][k] || arr[1][j] == arr[2][k]) {
} else {
// If all are different, consider this set as candidate
var found = false;
history.forEach(function(entry) {
if (
// The various possible sequences for comparison
(arr[0][i] == entry[0] && arr[1][j] == entry[1] && arr[2][k] == entry[2]) ||
(arr[0][i] == entry[0] && arr[1][j] == entry[2] && arr[2][k] == entry[1]) ||
(arr[0][i] == entry[1] && arr[1][j] == entry[0] && arr[2][k] == entry[2]) ||
(arr[0][i] == entry[1] && arr[1][j] == entry[2] && arr[2][k] == entry[0]) ||
(arr[0][i] == entry[2] && arr[1][j] == entry[0] && arr[2][k] == entry[1]) ||
(arr[0][i] == entry[2] && arr[1][j] == entry[1] && arr[2][k] == entry[0])
) found = true;
});
// If not found, add to history
if (!found) history[history.length] = new Array(arr[0][i], arr[1][j], arr[2][k]);
}
}
}
}
alert ("Count: " + history.length);
}
countcombinations(); // Gives 165
</script>
Mathematically, the problem can be solved by iterating the first set and considering the possibilities for each. This is what the algorithm in the above javascript code does. The sequence of the elements do not matter. Each time a new unique combination is found, it appends this combination to the history. For fun, you could experiment with the above code and apply the math below to understand it further.
For a human approach to the math, let's consider the first set and use (X, Y, Z) to denote the three chosen numbers. The numbers in each set need to be in increasing order and the smallest number in subsequent sets also need to be in increasing order. The array given to us meets these conditions. So, for the first set:
[2,3,4,5,6,7,8,9,10]
When X = 2, i.e., (2, Y, Z), there are two possibilities to consider:
Y = 3
Y > 3
If Y = 3, then Z has 9 possibilities (4 through to 12).
If Y > 3, then if Z < 12, (Y, Z) has 8C2 (8 choose 2), or 28, possible combinations.
If Z = 12, then (Y, 12) has 8 possible combinations. Thus:
X = 2, Y = 3, Z > 3 : 9 combinations
X = 2, Y > 3, Z < 12 : 8C2 = 28 combinations
X = 2, Y > 3, Z = 12 : 8 combinations
In short, there are 45 possible combinations where any of the numbers is 2.
Phew! Moving on, from numbers 3 to 9, you can reuse two conditions above: whether Z < 12 or Z = 12. You will see a pattern emerging as shown below:
X = 3, Y > 3, Z < 12: 8C2 = 28 combinations
X = 3, Y > 3, Z = 12: 8 combinations
X = 4, Y > 4, Z < 12: 7C2 = 21 combinations
X = 4, Y > 4, Z < 12: 7 combinations
...
X = 9, Y > 9, Z < 12: 2C2 = 1 combination
X = 9, Y > 9, Z = 12: 2 combinations
Finally, when X = 10, there is only 1 possibility that is (10, 11, 12).
Thus the number of combinations (with a nice decreasing pattern) is:
X = 2: 9 + 8C2 + 8 = 45
X = 3: 8C2 + 8 = 36
X = 4: 7C2 + 7 = 28
X = 5: 6C2 + 6 = 21
X = 6: 5C2 + 5 = 15
X = 7: 4C2 + 4 = 10
X = 8: 3C2 + 3 = 6
X = 9: 2C2 + 2 = 3
X = 10: 1 = 1
TOTAL: 165
Assume i have a function checkTime like the one below where i have to check for multiple condition simultaneously.
var result=0;
function checkTime(time1, time2) {
if (time1 >= 0 && time2 <= 0) {
result = 1;
}
else if (time1 >= 0 && time2 <= 1) {
result = 4;
}
else if (time1 >= 2 && time2 <= 3) {
result = 5;
}
else if (time1 >= 4 && time2 <= 6) {
result = 6;
}
else if (time1 >= 7 && time2 <= 9) {
result = 7;
}
else if (time1 >= 11 && time2 <= 12) {
result = 8;
}
else if (time1 >= 13 && time2 <= 15) {
result = 9;
}
else if (time1 >= 16 && time2 <= 17) {
result = 10;
}
else if (time1 >= 19 && time2 <= 20) {
result = 11;
}
return result;
}
(The above given example is hypothetical)
The function i have used totally works,but:
Is there a better method or procedure or formula to replace this?(where it doesnt have to be this lengthy or ugly)
Thanx!
You can use an array to represent all the combination:
tests = [
{ time1: 0, time2: 0, result: 1 },
{ time1: 0: time2: 1, result: 4 },
...
];
for (var i = 0; i < tests.length; i++) {
if (time1 >= tests[i].time1 && time2 <= tests[i].time2) {
return tests[i].result;
}
}
If the code is identical, and only the values change, you could do something like this:
function checkTime(time1, time2) {
[
[0, 0, 0],
[0, 1, 0]
].forEach(function (it) {
if (time1 >= it[0] && time2 <= it[1]) {
return it[2];
}
});
}
Well, first off you have the possibility of an undefined result, so that makes things ambiguous. Should result start at 0 instead? This is an important detail. Second, you seem to be working with boundaries, so it would help to change the <= to < to make this clearer. (If so, the 7-9/11-12 section has a bug.) Third, you have an implicit comparison of time1 and time2, so make that explicit.
var result = 0;
var diff = time2 - time1;
var bounds = [21, 19, 16, 13, 11, 7, 4, 2, 0];
if (diff <= 0) result = 0; // unexpected outcome
else
for (position = 1; position < bounds.length; ++position) {
if (time1 >= bounds[position]) {
if (time2 < bounds[position - 1]) {
result = 3 + (bounds.size - position);
}
break;
}
}
return result;
Other implementations are possible, but it's hard to tell based on your question exactly what problem you're solving.
follow-up
This section of code has a gap:
else if (time1 >= 7 && time2 <= 9) {
result = 7;
}
else if (time1 >= 11 && time2 <= 12) {
result = 8;
}
If time = 10 and time2 = 10, there is no match. It's easy to miss this type of error when you are repeating yourself. Specifying lower and upper bounds for each condition is unnecessary repetition. Since I couldn't see a pattern to the bounds (which could be delegated to a function), I just put the lower bounds into an array and made sure it was sorted descending so that the loop could stop after the first match.