Add up safe navigation array lengths - javascript

I have a count that starts at 0:
let count = 0;
And then I'm also getting several arrays which may or may not exist on an object and I wish to add these to the count:
count += currentData?.subjects?.length + currentData?.delivery_method?.length
I'm using the safe navigation operator since they may or may not exist.
However when I return the count:
return count;
I'm getting NaN (Not a number). How can I resolve this?

You are getting NaN because whenever one of the values is not defined, you add undefined to count.
You should default the values to 0:
count += currentData?.subjects?.length ?? 0;
count += currentData?.delivery_method?.length ?? 0;

Related

Why do I get an endless loop using moment.js?

I got the following string-array:
cohortsDates =
[
'2020-11', '2021-01',
'2021-02', '2021-03',
'2021-04', '2020-10',
'2021-05', '2020-12',
'2021-07'
]
Now I try to sort it that the dates are in an ascending order from 2020-10 to 2021-07 with this code:
cohortsDates.forEach((month) => {
for(var i = 0; i < cohortsDates.length; i++ ) {
if(moment(cohortsDates[i+1]) < moment(cohortsDates[i])) {
var swap = cohortsDates[i]
cohortsDates[i] = cohortsDates[i+1]
cohortsDates[i+1] = swap
}
}
})
console.log(cohortsDates)
But all I get is an endless loop and the sorted array never prints out. Does somebody know, what can I do to fix it?
When i === cohortsDates.length-1 (i.e. you are looking at the last item in your for loop) you test:
if(moment(cohortsDates[i+1]) < moment(cohortsDates[i])) {
Where cohortsDates[i+1] will always be undefined and thus less than the previous value.
So you swap them and assign cohortsDates[i] to cohortsDates[i+1].
This increases cohortsDates.length by 1 so the end condition of the for loop doesn't apply.
You now loop again and cohortsDates[i+1] is still undefined so it goes infinite.
JS has a built-in sort method to do this. Don't reinvent the wheel.
You don't exactly have the most efficient sorting algorithm. When sorting, you should use javascript's array.sort:
cohortsDates.sort((a, b) => moment(a) < moment(b) ? -1 : 1);
The sort method will loop over the array and call the function you passed as an argument with two of the values as parameters. If the function returns a negative number, that means a is less than b, a positive number means a is greater than b.

How to use an element of an array as an argument of indexOf method?

I have two arrays of values. I want to use the elements of one array to be the argument of an indexOf function. But I get a -1 (meaning value not found) even when I know the value exists in the array.
I have tested this by hard coding the value in the argument of indexOf so I know in this case that my problem is with cur_data variable. When I hard code the cur_data[x] with 'xyz' the indexOf returns correct index however when I use the array value [xyz] it returns -1.
What am I doing wrong?
function iterateSheets() {
var price_data = SpreadsheetApp.openById('1Nttb7XqUlZwGtmwbcRc3QkY3f2rxx7XdsdEU3cK4K4').getSheetByName('price').getRange("A2:A353").getValues()
var price_data2 = price_data.map(function(r) {
return r[0];
});
var test = new Array(30)
var ts = SpreadsheetApp.openById('18qFvVMVEE1k5DWUYaSezKeobcLr8I4oAmHLUpd_X99k');
var allShts = ts.getSheets();
for (var i = 0; i < 1; i++) //allShts.length //need to add in code to make sure tab is one of the fcst tabs
{
var cur_data = allShts[i].getRange("B8").getValues()
if (allShts[i].getName() == "July" || allShts[i].getName() ==
"Aug" || allShts[i].getName() == "Sept") {
for (var x = 0; x < 1; x++) {
Logger.log(cur_data[x])
Logger.log(price_data2.indexOf(cur_data[x]));
}
}
}
}
2D Array of values
getValues() method returns a two-dimensional Array of values from the Range that should be accessed via the values[row][column] schema. The for loop only increments the first dimension, that is, rows, and never accesses the value via column reference. Thus, you end up passing an Array instance to the indexOf() method.
Modification
You can add a second for loop to iterate over each of the elements of the Array of values, plus modify the first loop to make it more flexible just in case you ever need to loop over multiple rows:
for (var x = 0; x < cur_data.length; x++) {
for (var y = 0; y < cur_data[x].length; y++) {
Logger.log(cur_data[x][y])
Logger.log(price_data2.indexOf(cur_data[x][y]));
}
}
Comparison
indexOf() method performs search via the strict equality comparison and here is where the fun part starts. As Array instances are also Objects, meaning the same rules of comparison that apply to objects apply to them. This means that no two objects are equal (take a look at the comparison result table).
Useful links
getValues() reference;
indexOf() MDN reference;
Equality comparisons guide;

Losing the logic in my Javascript program

I have an issue with Javascript. I want to make the program , which generates 6 random numbers ( from 1 to 49 ).Anyway im doing it with for loop and it works. But when I try to find if any of those 6 numbers has a duplicate number , to replace it with a new random number. I've looked at js array duplicate functions , Im applying it , but it seems my fault is in the logic , still beginner with js , so please help me :)
js code:
var arr = new Array(7);
var numbers = [];
for(var i=1; i<arr.length;i++){
numbers[i] = Math.floor(Math.random()*48)+1;
if(numbers[i] == numbers[i+1]){
arr.indexOf(numbers[i]);
arr.push(Math.floor(Math.random()*48));
}
}
i`m trying to apply indexOf ( to find the duplicate number's index, and after that to replace it with new element via push()). Please give me some advises how to improve on my programming logic skills :(
First, JavaScript arrays are zero-indexed, so the first index is 0, not 1. Although it is valid to skip the first index and use the second to seventh indices instead, you will usually want to avoid that.
Second, why do you check numbers[i] against numbers[i + 1]? At that point, numbers[i + 1] won't have been set yet as it will be set in the next iteration. Additionally, even if this problem did not exist, your code would fail to catch this sequence: [ 5, 3, 5 ].
Third, I am not quite sure why you use two different arrays. Although you definitely can, there is no good reason to do so.
I will suggest another way to solve your problem and add comments to the steps necessary. I hope these will help you to understand this solution to your problem.
// Create an array for the chosen numbers
var numbers = [];
// Loop to generate 6 elements, zero-based
for(var i = 0; i < 6; i++) {
// This variable will hold the number we choose
var number;
// This is a do-while loop which generates a random integer
do {
// Generate a random integer between 1 and 49 (incl.)
number = Math.floor(Math.random() * 48) + 1;
} while(numbers.indexOf(number) != -1); // Exit loop once the number is not contained in the array
// Store the number in the array
numbers[i] = number;
}
This algorithm is short (8 SLOC) and pretty efficient.
This is one way you could do it:
var numbers = [];
for(i=0;i<6;i++){
numbers[i] = Math.floor(Math.random()*48)+1;
//this loop will help us check all of the already assigned numbers.
for(x=0;x<i;x++){
if(numbers[x] == numbers[i]){
i--;
}
}
}
This might not be the most efficient and optimal way to do it, but it sure works and it will give you no repetition on the numbers now that it will loop untill it finds a different number. Its a fast and short solution, yet not the most efficient. Hope this helps =).
for (var a=[],i=1;i<40;++i) a[i]=i;
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
a = shuffle(a);
Random Numbers , No repeats
This statement will never be true:
if(numbers[i] == numbers[i+1]){
When you start, numbers array is empty. Then you assign one random to element 0. Then you try to compare element 0 in numbers to 1, but numbers[1] will be undefined.
The best way to do this is to start with an empty arr array, meaning length of 0. Loop until length equals 6. In the loop, generate a random number and only push the value into the arr array if the number doesn't exist in there.
UPDATE:
After seeing some of the answers, I think this is probably the simplest:
var arr = [],
num;
while (arr.length < 6) {
num = Math.floor(Math.random() * 48) + 1;
if (arr.indexOf(num) === -1) {
arr.push(num);
}
}
console.log(arr);

confused about indexOf(array[0])

This is for a coderbyte challenge that I finished a while ago. Your function is supposed to get the second greatest and second lowest numbers heres what I originally used:
function SecondGreatLow(arr){
var sorted = arr.sort(function(a,b){
return a-b;
});
return sorted[1] + " " + sorted[arr.length - 2];
}
SecondGreatLow(readline());
I got two cases wrong with this function one of them being ([2,2,2,5,5,5,6]) because there are duplicate numbers. I implemented this into my new code :
function SecondGreatLow(arr) {
var exclude = [arr[0]];
for(var i = 1; i < arr.length; i++) {
if (exclude.indexOf(arr[i]) == -1) {
exclude.push(arr[i]);
}
}
return exclude
}
SecondGreatLow([33,33,33,44,44,44,55,55,6,4,3])
My question is how does this find all the duplicate numbers? Isn't the variable exclude(arr[0]) 33? I'm confused how this gets rid of all the duplicate numbers.
My question is how does this find all the duplicate numbers?
It actually doesn't find duplicate values, it collects unique values.
You iterate over the original array and add only values to exclude which are not already in exclude. That guarantees that every value in exclude is unique.
My question is how does it do this if exclude originally is only arr[0] or 33
This line:
var exclude = [arr[0]];
it just used to not start with an empty array. As you can see in the for statement
for(var i = 1; i < arr.length; i++) {
the code starts iterating at index 1, not 0. You could also just have started with an empty array:
var exclude = [];
for(var i = 0; i < arr.length; i++) {
but that's a bit wasteful since you know that the first value is going to be added to exclude anyway.
The way this code works is but adding new variables into the array exclude. It will only add the new variable if the current variable doesn't already exist in the array exclude.
The line:
exclude.indexOf(arr[i]) == -1
is what is doing main work.
It finds variable i and check if it already exists in the array exclude. If it does not exist indexOf returns -1, thus the if statement is true, and we go into the line
exclude.push(arr[i]);
and add the variable into the array exclude.
In the below loop
for(var i = 1; i < arr.length; i++) {
if (exclude.indexOf(arr[i]) == -1) {
exclude.push(arr[i]);
}
}
It is checking if the number from input array is already present in exclude if not it will insert that number to exclude.
The indexOf() method searches the array for the specified item, and returns its position. so in case of duplicate numbers it will not return -1 thus the logic above will skip adding that duplicate number to exculde variable. You can refer below for more details.
http://www.w3schools.com/jsref/jsref_indexof_array.asp

Can I limit the length of an array in JavaScript?

I want to display the product browsing history, so I am storing the product ids in a browser cookie.
Because the list of history is limited to 5 items, I convert the cookie value to an array, then check the length of it and cut the redundant.
The code below is what I have tried, but it does not work; the array item isn't removed.
I would like to ask how to limit the array length so it can only store 5 items?
Or
How can I cut the items after the array index 4?
var id = product_id;
var browseHistory = $.cookie('history');
if (browseHistory != null) {
var old_cookie = $.cookie('history');
var new_cookie = '';
if (old_cookie.indexOf(',') != -1) {
var arr = old_cookie.split(',');
if (arr.length >= 5) {
arr.splice(4, 1)
}
}
new_cookie = id + ',' + old_cookie;
$.cookie('history', new_cookie, { expires: 7, path: '/' });
} else {
$.cookie('history', id, { expires: 7, path: '/' });
}
You're not using splice correctly:
arr.splice(4, 1)
this will remove 1 item at index 4. see here
I think you want to use slice:
arr.slice(0,5)
this will return elements in position 0 through 4.
This assumes all the rest of your code (cookies etc) works correctly
The fastest and simplest way is by setting the .length property to the desired length:
arr.length = 4;
This is also the desired way to reset/empty arrays:
arr.length = 0;
Caveat: setting this property can also make the array longer than it is: If its length is 2, running arr.length = 4 will add two undefined items to it. Perhaps add a condition:
if (arr.length > 4) arr.length = 4;
Alternatively:
arr.length = Math.min(arr.length, 4);
arr.length = Math.min(arr.length, 5)
var arrLength = arr.length;
if(arrLength > maxNumber){
arr.splice( 0, arrLength - maxNumber);
}
This solution works better in a dynamic environment like p5js. I put this inside the draw call and it clamps the length of the array dynamically.
The problem with
arr.slice(0,5)
is that it only takes a fixed number of items off the array per draw frame, which won't be able to keep the array size constant if your user can add multiple items.
The problem with
if (arr.length > 4) arr.length = 4;
is that it takes items off the end of the array, so which won't cycle through the array if you are also adding to the end with push().
I think you could just do:
let array = [];
array.length = 2;
Object.defineProperty(array, 'length', {writable:false});
array[0] = 1 // [1, undefined]
array[1] = 2 // [1, 2]
array[2] = 3 // [1, 2] -> doesn't add anything and fails silently
array.push("something"); //but this throws an Uncaught TypeError
I was surprised nobody mentioned the following snippet to limit the length of the array:
arr.splice(5);
According to the Parameters definitions for splice, if start is larger than the length of the array, it will be set to the length of the array, and if deleteCount is omitted or larger than the array length, all of the items after start will be deleted.
Therefore, if you want to limit an array to some MAX_SIZE (modifying the existing array instead of creating a new instance) an easy shortcut is just arr.splice(MAX_SIZE).
As others have said, there is more going on with the code in the question, but given the title and spirit of the ask, I hope this is a useful answer for anyone else ending up here via search.
Note: According to the compatibility notes for IE 5.5-8, deleteCount does not work as described above, so this solution won't work right on those browsers.
You need to actually use the shortened array after you remove items from it. You are ignoring the shortened array.
You convert the cookie into an array. You reduce the length of the array and then you never use that shortened array. Instead, you just use the old cookie (the unshortened one).
You should convert the shortened array back to a string with .join(",") and then use it for the new cookie instead of using old_cookie which is not shortened.
You may also not be using .splice() correctly, but I don't know exactly what your objective is for shortening the array. You can read about the exact function of .splice() here.
Came here but couldn't find a functional way of limiting the length of an array.
So I came up with:
const list = ["a","b","c","d","e","f","g","h","i"];
const listWithOnly3Items = list.filter((element,index) => index < 3);

Categories