How to update array in function - javascript

i have some problem with my array
let me explain the issue
first i want you guys see my codes to be on the same page
Note: this example code (not full version)
Code:
let checked = []
$.get("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&page=1&per_page=5", status => { createDiv(status, checked)})
function createDiv(status, checked) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}})}
So when i click on checked it's push to array object and if i click on the same checked button it's filter the array because is already in array.
the problem is the array let checked = [] only update inside the function, what that means if i console.log checked inside function the array is filtered fine but if i console.log checked outside the array is not update
how can i update the array also outside function
i hope you guys have solution for me :)

You declared checked in the function head. So checked exists in local scope. I think if you change the first checked from let checked = [] to var checked = [] and remove the checked in function head it should work.
var checked = []
function createDiv(status) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}})}

The array will updated outside because it is a reference to a Array. The problem here is you must get data of the array after you click. It mean you must get data in callback at click function. Because this is a async so you only get data at the callback function if you want data is data that after clicked.
let checked = []
$.get("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&page=1&per_page=5", status => { createDiv(status, checked)})
function createDiv(status, checked) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}
console.log(checked);})}

Related

Only push the object which is not in the array yet

This is my function:
const multiSelect = value => {
let tmpArr = [...selectedPeople];
if (tmpArr.length === 0) {
tmpArr.push(value);
} else {
tmpArr.map(item => {
if (item.id !== value.id) {
tmpArr.push(value);
} else {
return;
}
});
}
setSelectedPeople(tmpArr);
};
I want to check the array for the new value by comparing it with all items. If value === item item the loop function should return, but if the value is not in the array yet, it should push it.
This is a big problem for me but I assume it is a small problem for you guys.
Use Array.every() to check if the array doesn't contain an item with the same id:
const multiSelect = value => {
const tmpArr = [...selectedPeople];
if(tmpArr.every(item => item.id !== value.id)) {
tmpArr.push(value);
}
setSelectedPeople(tmpArr);
};
However, this means that you're duplicating the array needlessly, while causing a re-render, that won't do a thing. So check if the item is already a part of selectedPeople by using Array.some(), and if it does use return to exit the function early. If it's not continue with cloning, and updating the state:
const multiSelect = value => {
if(tmpArr.some(item => item.id === value.id)) {
return;
}
const tmpArr = [...selectedPeople];
tmpArr.push(value);
setSelectedPeople(tmpArr);
};
Use find to check if the item is already in the array. Also, there's no need to make a copy of the source array:
const multiSelect = value => {
if (!selectedPeople.find(item => item.id === value.id))
setSelectedPeople(selectedPeople.concat(value))
}
Another approach.
const
multiSelect = value => setSelectedPeople([
...selectedPeople,
...selectedPeople.some(({ id }) => id === value.id)
? []
: [value]
]);

Template Component doesn't re-render when it is removed from array

Everything works fine when I append the template onclick. However when the remove events fire it deletes it from the array. It deletes it from the array but when I go to click to append it back to the array, an error fires off. Cannot read property 'content' of null at HTMLButtonElement.. I've double checked my Html and that seems fine. I don't know why it won't re-append or re-render after it was already pushed into the array and then deleted.
// Function to remove item from array onClick
function removeItem(shoppingItems, btns) {
btns.addEventListener('click', (e) => {
let newArr = shoppingItems.filter(item => item.id !== item.id)
shoppingList = newArr
cartMenuItems.innerHTML = newArr
cartItemCount.innerText = newArr.length
return newArr
})
}
// Template Component
btn.addEventListener('click', (e) => {
if (shoppingList.indexOf(cartItem) !== -1) {
return
}
else {
const menuTemplate = document.querySelector('#menu-template').content
const copyMenuTemplate = document.importNode(menuTemplate, true);
copyMenuTemplate.querySelector('.menu-item-title').textContent = cartItem.title;
copyMenuTemplate.querySelector('.menu-price').textContent = cartItem.price;
copyMenuTemplate.querySelector('.menu-img').src = cartItem.image;
const removeBtn = copyMenuTemplate.querySelector('.remove-btn')
cartMenuItems.appendChild(copyMenuTemplate);
shoppingList.push(cartItem)
cartItemCount.innerText = shoppingList.length;
removeItem(shoppingList, removeBtn);
}
I figured it out. I removed the the original menu template from the else statement. I was deleting the model template every time I clicked the button so it couldn't re-render null.

Checking a range of checkboxes between 2 boxes

I have a table of rows, each row has a checkbox. I have built a function that detects if the shift key had been pressed then, if it has then once 2 checkboxes are checked it will check the boxes between them.
The function works to some degree, however for some reason I have to check a third box to tick the boxes in-between.
How do I alter the code so it checks the boxes on the second check and not have to check a third?
Thanks in advance.
Below is my code.
Vue Data:
data: () => ({
keycheck: false,
checkRows: []
})
Created:
created() {
window.addEventListener('keydown', e => {
if (e.keyCode === 16) {
this.keycheck = true
console.log('The shift key is being held down...')
}
})
window.addEventListener('keyup', e => {
if (e.keyCode === 16) {
console.log('Upper')
this.keycheck = false
this.checkRows = []
//console.clear();
}
})
},
methods: {
checkbox(key) {
if (this.keycheck) {
this.checkRows.push(key)
if (this.checkRows.length === 2) {
console.log(this.checkRows)
for (let i = this.checkRows[0]; i <= this.checkRows[1]; i++) {
let bData = this.displayed_array
bData[i]['rowCheck'] = true
console.log('test')
}
}
}
},
},
There doesn't seem to be anything wrong with the code. However, right now if you're not holding shift, the first click is not registered in the checkRows variable. So you have to hold shift on the first click for it to work in two clicks.
if (this.keycheck) {
this.checkRows.push(key);
I replicated it in this codepen: https://codepen.io/CodingDeer/pen/pozbadW
FYI it's also better to set the array to empty using this.checkRows.length = 0; because it doesn't make a copy of the array.
I figure this out. By adding this.$forceUpdate() it updates the data. This was previously done by the third click.
methods: {
checkbox(key) {
if (this.keycheck) {
this.checkRows.push(key)
if (this.checkRows.length === 2) {
console.log(this.checkRows)
for (let i = this.checkRows[0]; i <= this.checkRows[1]; i++) {
let bData = this.displayed_array
bData[i]['rowCheck'] = true
console.log('test')
this.$forceUpdate()
}
}
}
},

Filter function don't return values

I would filter a list of objects using filter in JS
let dataid = infosStructured.filter(elm => {
console.log(elm.identifiant);
console.log("matching value", elm.identifiant == this.dataService.getcurrentId()); // I get a true value
elm.identifiant == this.dataService.getcurrentId();
});
console.log(dataid); // empty
My dataid is empty whereas elm.identifiant == this.dataService.getcurrentId() is true at a given moment
It has nothing to do with functional-programming. Try below code
let f = a => { a == 1 };
f(1); // undefined
let g = a => a == 1;
g(1); // true
let h => a => { return a == 1; };
h(1); // true
It's a good idea not to use inlined function to debug higher order function.
For multi line code inside Array.filter() you need to explicitly mention return. For single line like infosStructured.filter((elm) => elm) we do not specify the return keyword.
let dataid = infosStructured.filter(elm => {
console.log(elm.identifiant);
console.log("matching value", elm.identifiant == this.dataService.getcurrentId()); // I get a true value
elm.identifiant == this.dataService.getcurrentId();
return elm;
});
console.log(dataid);
If you do not need console.log() inside filter then you can change that in one liner without return keyword as:
let dataid = infosStructured.filter((elm) => elm.identifiant == this.dataService.getcurrentId());
console.log(dataid);

indexof - check if a value is already in array with javascript)

I am working with angular and I am trying to create a "select all" button.
I have a list of items, each item has a toggle and what I am doing is, on change (everytime the toggle changes from true (selected) to false (not selected), I run a function to create an array with all the IDs of the selected elements.
This works almost perfectly, the problem is that I am facing some issues with the indexfOf method to check if the ID is already in the array.
var isInArray;
isInArray = function(arr, id) {
console.log("index of ", arr.indexOf(id));
return arr.indexOf(id);
};
scope.evtSelectAll = function() {
return angular.forEach(scope.listToDisplay, function(element) {
element.copyTo = true;
return scope.selectFromList(element.iID, element.copyTo);
});
};
scope.selectFromList = function(id, copy) {
if (copy === true && isInArray(scope.selected, id) === -1) {
scope.selected.push(id);
} else {
scope.selected.pop(id);
}
console.log("scope.selected - ", scope.selected);
if (scope.selected.length > 0) {
console.log("Emitted event: can proceed!");
scope.$emit('enough-elements');
} else {
console.log("Emitted event: can not proceed!");
scope.$emit('not-enough-elements');
}
return scope.result = scope.selected;
};
the problem I've got is when the array (scope.selected) has multiple IDs.
Let's say, for example, that my scope.selected looks like this:
scope.selected = [2,3,4,7]
if I click on select all, nothing gets added (and this is correct)
Now, let's say I untick 4 and 7 for example, and my scope.selected now looks like this:
scope.selected = [2,3]
If I now click on select all, my result is the following: [2,4,7].
I lose the 3
I think this is due to the fact that my array doesn't have one single item?
thanks for any help. Here's also a quick codepen to explain the problem. If you check the console and play with the toggles you should be able to see straight away what I am referring to.
Thanks in advance
Thanks to Matthias and Christian Bonato for their suggestions.
At the end, I solved using both of their suggestions and the final result seems to work as expected.
Here's a codepen with the final version: http://codepen.io/NickHG/pen/KNXPBb
Basically, I changed
scope.selected.pop(id);
with
$scope.selected.splice( isInArray($scope.selected, id),1);
and in the selectAll event function, I always empty scope.selected[] before adding elements to the array
$scope.evtSelectAll = function() {
$scope.selected = []
angular.forEach($scope.list, function(element) {
element.copyTo = true;
return $scope.selectFromList(element.id, element.copyTo);
});
};
thank you for your help!
I think mostly your code contains a logical error. You are using the function selectFromList to de-select (when done individually) and for the select all (which you don't want to use to de-select).
As someone pointed out in a for some reason now deleted answer, the pop.() function shouldn't be called with any arguments (it is only for removing the last element), you should use splice like this:
$scope.selected.splice( isInArray($scope.selected, id),1);
Unless you really need the emitted functionality to run on a select all, you can try if this is the answer for you:
var isInArray;
isInArray = function(arr, id) {
console.log("index of ", arr.indexOf(id));
return arr.indexOf(id);
};
scope.evtSelectAll = function() {
return angular.forEach(scope.listToDisplay, function(element) {
element.copyTo = true;
if (isInArray($scope.selected, element.id) === -1) {
$scope.selected.push(element.id);
}
});
};
scope.selectFromList = function(id, copy) {
if (copy === true && isInArray(scope.selected, id) === -1) {
scope.selected.push(id);
} else {
$scope.selected.splice(isInArray($scope.selected, id), 1);
}
console.log("scope.selected - ", scope.selected);
if (scope.selected.length > 0) {
console.log("Emitted event: can proceed!");
scope.$emit('enough-elements');
} else {
console.log("Emitted event: can not proceed!");
scope.$emit('not-enough-elements');
}
return scope.result = scope.selected;
};
Now the select all only adds to scope.selected if it doesn't find the id in the scope.selected list.

Categories