JSON.stringify() causing an infinite loop (Edit: NO. Bad logic is.) - javascript

My product object looks somewhat like this:
{name: 'abc', description: 'def', price: 100, quantity: 1, comment: '', customizations: []}
The customizations key is an array that has other such product objects in it. You may ignore it for this question. As you might have noticed, the comment and customizations keys are the keys that make the (theoretically) same product (practically) different when adding to cart.
I want to make a function to add such products to an array called cart_items[]. If the (practically) same product is chosen, I only want to increment the quantity inside the cart_items[i], else add a new object.
This is my function:
$scope.add_to_cart = function(product) {
// if the cart is empty, skip the brouhaha
if ($scope.cart_items.length === 0) {
$scope.cart_items.push(angular.copy(product));
} else {
for (var i = 0; i < $scope.cart_items.length; i++) {
// copy the original quantity and set it to 1 for comparison
var qty = $scope.cart_items[i].quantity;
$scope.cart_items[i].quantity = 1;
if (JSON.stringify(product) === JSON.stringify($scope.cart_items[i])) {
$scope.cart_items[i].quantity = qty + 1;
} else {
$scope.cart_items[i].quantity = qty;
$scope.cart_items.push(angular.copy(product));
}
}
}
};
The problem: First product adds successfully. Adding another causes an infinite loop. I replaced if(JSON...) with if(1 === 1) and the infinite loop didn't occur. I don't know where am I going wrong. Any help?

The problem you have is that you're increasing the size of the array in the loop and the stop condition is i being $scope.cart_items.length.
If your goal is to add the object if it's not yet present, what you want is probably this :
boolean found = false;
for (var i = 0; i < $scope.cart_items.length; i++) {
var qty = $scope.cart_items[i].quantity;
$scope.cart_items[i].quantity = 1;
if (JSON.stringify(product) === JSON.stringify($scope.cart_items[i])) {
$scope.cart_items[i].quantity = qty + 1;
found = true;
break;
}
}
if (!found) {
var item = angular.copy(product);
item.quantity = 1;
$scope.cart_items.push();
}
Note that two identical objects (i.e. objects with same property values) should not give the same JSON string because the order of property iteration isn't specified. It usually works (especially if the cloning is done the obvious way) but there's no guarantee. You really should compare based on the properties, not using JSON stringification.

You probably enter in a recursive loop, because of objects contained in customizations...
A [ customizations = B ]
B [ customizations = C ]
C [ customizations = A ]
----------------------------
Infinite loop

Related

Incrementing values in loop acting weird - JavaScript

I have a code that checks indexes of array with some ID's, and increases some order value for items with those ID's. The code looks something like this:
function changeOrder(initialIndex, newIndex, itemsMap, itemIds) {
const requestPayload = [];
itemsMap[itemIds[initialIndex]].order = itemsMap[itemIds[newIndex]].order;
requestPayload.push(itemsMap[itemIds[initialIndex]]);
if (initialIndex > newIndex) {
for (let i = newIndex; i < initialIndex; i++) {
itemsMap[itemIds[i]].order++;
requestPayload.push(itemsMap[itemIds[i]]);
}
}
console.log(requestPayload);
return requestPayload;
}
Now, the issue is when i hover over the requestPayload inside the console.log, order values are correct (0, 1, 2 in my case). But once it does the logging, or returns the requestPayload, values are 1, 2, 3. Function is triggered only once, so it doesn't increment them again. Here is the screenshot, you can see when I manually log the values when breakpoint is set vs logged payload.
EDIT: Solved with the help from #Pointy in the comments:
for (let i = newIndex; i < initialIndex; i++) {
const copy = { ...itemsMap[itemIds[i]] };
copy.order++;
requestPayload.push(copy);
}

A problem with array and checking the values inside

Hello recently I was working with arrays in JavaScript and appear a little problem with a function.
My problem is in the function checkRepeat, in this function I try to know if I click a Card before or not, when I click the first Card work but with the other cards they don't work, I was thinking that my problem maybe it's for the loop because maybe it's taking only one element but I don't know.
This is my code:
const cardArray = [
{name: 'fries'},
{name: 'pizza'},
{name: 'hotdog'},
{name: 'cheeseburger'},
{name: 'milkshake'},
{name: 'ice-cream'}
]
const emptyCard = [
{name: 'white'},
{name: 'blank'}
]
// the format of the img
const format = '.png'
// I select the place where I gonna put the board
const grid = document.querySelector('.grid');
const cardsChosen = [];
const cardsChosenId = [];
const createBoard = () =>{
for(let n = 0; n < cardArray.length*2; n++)
{
// I create the img
let card = document.createElement('img');
card.src = `images/${emptyCard[1].name}${format}`;
card.id = `card-${n}`
card.addEventListener('click',flipCard)
grid.appendChild(card)
}
}
function flipCard(){
// I get the id, just the number
let cardId = parseInt(this.getAttribute('id').slice(5,7));
const v1 = checkRepeat(cardsChosenId,cardId);
console.log("Value: ",v1)
//console.log("Values in the array: ",cardsChosenId)
}
function checkRepeat(array,value){
if(array.length === 0){// if the array is empty
array.push(value);//push in the array the value
return true;// and return true
}
else{// if isn't
for(let i = 0; i < array.length; i++){// walk through the array
if(array[i] === value){// check if the values of the array === value
return false;
}
else{// if the value and the array values are different
array.push(value);//push the value
return true;
}
}
}
}
createBoard();
I found the error in the code, the problem was the logic.
Because when it traversed the array, it finds an equal value, but it continues traversing the array and the other values ​​do not resemble the compared value, so it adds the same values ​​to me later in the array.
I explain:
When I click on each of the cards, they save the id in an arrangement, and if I click again on a card that I had previously given it, it would compare with each element and when it found the same one, it would return false, but continued scrolling the whole arrangement and added more elements to the arrangement when I shouldn't have.
Example:
I click on the 1st element, [0] is saved, I click on the second [0,1] and I give the second element again and when comparing 0 with 1 I find that they are different and it remains like this [0,1,1] when it should return only [0,1]
I resolve with includes:
function checkRepeat(array,value){
if(array.includes(value)){//return true if value it's in the array
return false;
}
else{
array.push(value);
return true;
}
}

Add/sum multiple values in array with same id

So lets say i have the following array.
[{idProduct:1, unitCost:400},
{idProduct:1, unitCost:160},
{idProduct:1, unitCost:46},
{idProduct:2, unitCost:200},
{idProduct:2, unitCost:40}
I cant seem to find the way to add the unit cost of product 1 and then cut off to add the other units cost of product 2 and so on... I actually want to append it to a different row in google spreadsheet but I first want to calculate it.
I tried some for/if loops but i always end up with the total cost for all products, not just individual ones.
This for gives out the total cost for all ids. When I palce an if inside this for, Im not sure how to compare the two ids, plus Im supposed to set totalCost back to 0 if i want to reset the sum
for (var a = 0; a < arr.length ; a++) {
totalCost= totalCost+ arr[a].unitCost;
}
I tried an if with a break but that doesnt seem to work either, wanted to maybe try promises but Im not sure that would help. I also tried to set another for that compares the next ids to the one im currently in but that seemed to complicate things and the numbers i got were wrong.
I also tried
for (var a = 0; a < arr.length ; a++) {
if(arr[a].idProduct === arr[a+1].idProduct){
totalCost= totalCost+ arr[a].unitCost;
}
}
But of course on the last id it compares it to undefinded since a+1 is longer than the length of the array.
Im honestly burned out by this by now, and im pretty sure its a simple solution, but I really dont know what else to do
You can use reduce to summarize the array and add the values into an object using the idProduct as the key. Use Object.values to convert the object into an array.
let arr = [{"idProduct":1,"unitCost":400},{"idProduct":1,"unitCost":160},{"idProduct":1,"unitCost":46},{"idProduct":2,"unitCost":200},{"idProduct":2,"unitCost":40}]
let result = Object.values(arr.reduce((c, {idProduct,unitCost}) => {
c[idProduct] = c[idProduct] || {idProduct,unitCost: 0};
c[idProduct].unitCost += unitCost;
return c;
}, {}));
console.log( result );
Or you can use the idProduct as the key like and the sum as the value like:
let arr = [{"idProduct":1,"unitCost":400},{"idProduct":1,"unitCost":160},{"idProduct":1,"unitCost":46},{"idProduct":2,"unitCost":200},{"idProduct":2,"unitCost":40}]
let result = arr.reduce((c, {idProduct,unitCost}) => {
c[idProduct] = c[idProduct] || 0;
c[idProduct] += unitCost;
return c;
}, {});
console.log(result);
You could use a .forEach() like below. This code adds e.unitCost to the object with the same idProduct
var input = [{
idProduct: 1,
unitCost: 400
},
{
idProduct: 1,
unitCost: 160
},
{
idProduct: 1,
unitCost: 46
},
{
idProduct: 2,
unitCost: 200
},
{
idProduct: 2,
unitCost: 40
}
];
var output = { };
input.forEach(e => output[e.idProduct] = (output[e.idProduct] || 0) + e.unitCost);
console.log(output);
let pro;
let unitcost=[];
for (var a = 0; a < arr.length ; a++) {
if(pro==arr[a].idProduct){
unitcost[unitcost.length-1]+=arr[a].unitCost;
}
else{
pro=arr[a].idProduct;
unitcost.push(pro);
unitcost.push(arr[a].unitCost);
}
}
console.log(unitcost);

Can I use a loop to optimize my code?

I'm trying to optimize my code to be more efficient and easier to read. I have some combined if-statements, which I think could be better, if they are transformed to for-loops, I'm just not sure how to do this?
This is my code:
if (starportSelected){
if(game.currentLevel.requirements.vehicles.indexOf('transport')>-1 && cashBalance>=vehicles.list["transport"].cost){
$("#transportbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('scout-tank')>-1 && cashBalance>=vehicles.list["scout-tank"].cost){
$("#scouttankbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('heavy-tank')>-1 &&cashBalance>=vehicles.list["heavy-tank"].cost){
$("#heavytankbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('harvester')>-1 && cashBalance>=vehicles.list["harvester"].cost){
$("#harvesterbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.aircraft.indexOf('chopper')>-1 && cashBalance>=aircraft.list["chopper"].cost){
$("#chopperbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.aircraft.indexOf('wraith')>-1 && cashBalance>=aircraft.list["wraith"].cost){
$("#wraithbutton").removeAttr("disabled");
}
}
I think first step would be to create two arrays, one for vehicles and one for aircrafts like this:
var vehicles = ['transport', 'scout.tank', 'heavy-tank', 'harvester'];
var aircraft = ['chopper', 'wraith'];
But how to take the rest of the code and change it to for-loop seems like a hard case for me.
All help and explanation would be highly appreciated!
Seems that you have "vehicles" and "aircraft" types, with multiple values for each.
As such, I'd create an object of types to arrays of values.
Because you're also using a variable named vehicles and aircraft, you'll want to reference those in a separate object so that you can look them up with a string.
var lists = {
vehicles: vehicles,
aircraft: aircraft
}
var obj = {
vehicles: ["transport", "scout-tank", "heavy-tank", "harvester"],
aircraft: ["chopper", "wraith"]
};
Then use an outer and inner loop.
// v---("vehicles" or "aircraft")
for (var type in obj) { // v---("transport", "scout-tank", "chopper", etc...)
obj[type].forEach(function(val) {
if(game.currentLevel.requirements[type].indexOf(val)>-1 &&
cashBalance >= lists[type].list[val].cost) {
$("#" + val.replace("-", "") + "button").removeAttr("disabled");
}
});
}
Notice also that I had to replace the hyphen in the ID selector, since it wasn't used as part of the ID.
The two objects at the top could be combined into a single one if you wish:
var obj = {
vehicles: {
list: vehicles,
values: ["transport", "scout-tank", "heavy-tank", "harvester"]
},
aircraft: {
list: aircraft,
values: ["chopper", "wraith"]
}
};
Then adjust the loop references accordingly.
I've also cached the objects for performance, as Jared noted above.
for (var type in obj) {
var list = obj[type].list;
var requirements = game.currentLevel.requirements[type];
obj[type].values.forEach(function(val) {
if(requirements.indexOf(val)>-1 && cashBalance >= list[val].cost) {
$("#" + val.replace("-", "") + "button").removeAttr("disabled");
}
});
}
To make it even more efficient than the original, we'll drop some of the jQuery calls.
for (var type in obj) {
var list = obj[type].list;
var requirements = game.currentLevel.requirements[type];
for (var i = 0, vals = obj[type].values; i < vals.length; i++) {
var val = vals[i];
if(requirements.indexOf(val) > -1 && cashBalance >= list[val].cost) {
document.getElementById(val.replace("-", "") + "button").disabled = false;
}
}
}

Search for the key in the given array of objects and replace the value;

Implementing the multiple sort functionality; Where need to toggle the array which hold the sorting fieldname and sorting order;
Example
Click Sort By Name:
[{"sortKey":"name","sortValue":"desc"}]
Again Click Sort By Name:
[{"sortKey":"name","sortValue":"asc"}]
Click Sort By Age:
[{"sortKey":"name","sortValue":"asc"},{"sortKey":"age","sortValue":"desc"} ]
Again Click Sort By Name:
[{"sortKey":"name","sortValue":"desc"},{"sortKey":"age","sortValue":"desc"} ]
DEMO
if (checkIfObjectExists($scope.sortList, sortingObject)) {
if (!$scope.sortList.hasOwnProperty(sortingObject.sortType)) {
console.log($scope.sortList);
// replace the value for the key
}
} else {
$scope.sortList.push(sortingObject);
}
I changed some things in your implementation. Problem was you were checking if the whole object is not same then push in the array. But what you need if sorKey is same reverse the sortValue.
DEMO
Changed your function checkIfObjectExists to updateArray.
function updateArray(array, newObject) {
var i = 0;
for (i = 0; i < array.length; i++) {
var object = array[i];
if (object.sortKey == newObject.sortKey) {
object.sortValue= (object.sortValue==='asc')?'desc':'asc';
return;
}
}
array.push(newObject);
}
And while calling I will just call like this in $scope.clickMe.
updateArray($scope.sortList, sortingObject);

Categories

Resources