How does modifying an array changes another array in JS - javascript

I want someone to help me understand how does changing the item array changes the orderList array in the function below?
The function below is used to increment and decrement the quantity of the order
using "+" and "-" buttons, if the action is "+" then add 1 to quantity else minus 1 to quantity
const [orderItems, setOrderItems] = React.useState([])
const editOrder = (action, menuId, price) => {
let orderList = orderItems.slice()
let item = orderList.filter(a => a.menuId == menuId)
if(action == "+"){
if(item.length > 0)
let newQty = item[0].qty + 1
item[0].qty = newQty
item[0].total = item[0] * price
} else {
const newItem = {
menuId: menuId,
qty: 1,
price: price,
total: price
}
orderList.push(newItem)
}
setOrderItems(orderList)
} else {
if(item.length > 0) {
if (item[0].qty > 0){
let newQty = item[0].qty - 1
item[0].qty = newQty
item[0].total = newQty * price
}
}
setOrderItems(orderList)
}
}

In JS when you assign a value of object type (e.g. object literal, array, functions) to a variable say x, basically what gets assigned to x is a reference to the original value.
If I have
let a = {a: 123}; // _a_ is object
let b = a; // Now _b_ has a reference to it
b.a = 312; // Essentially here we are modifying _a_
console.log(a.a)
You see we managed to change a via b. Same thing can happen with arrays;
let a = [{a: 123}]; // Array is also object type
let b = a; // Same logic applies here, _b_ points to _a_ basically
b[0].a = 312;
console.log(a[0].a)

That's because Arrays are non-primitives.
You need to imagine variable as wires to values. Every primitive type is imutable, so when you try to change the value of that variable, you change the "wire" that it points to, which is another unique value.
Every other non primitive value is mutable, so when you change the value of it, every other variable that is pointing to that value will now return that value changed.
If I do:
const fruit = { name: 'apple' } // {} creates a new value and points to it
const number = 1; // points to the primitive value 1
so if I do:
const anotherFruit = fruit;
anotherFruit.name = 'banana'; // changes the value of fruit
let anotherNumber = number;
number = 3; // value 1 is a primitive, so it can't be changed, will point to value 3
There's a small course by Dan Abramov that will make it easier for you to understand it: https://justjavascript.com

Related

State not calculating numbers correctly - just stringing them together [duplicate]

This question already has answers here:
Adding two numbers concatenates them instead of calculating the sum
(24 answers)
Closed 8 days ago.
So i feel like an idiot. Im getting numbers strung together instead of added from my state. I cant seem to solve it using normal update state methods.
Its for component that selects how many of an item to buy from an invintory. Everything worked when i had on the option to buy one at a time. Now i have added the 'puchaseAmount' state that should control it. and the quantity breaks and strings '010101010' if i try to buy 10 at a time. The same thing happens to the total items owned count. I feel im missing something simple and stupid lol
const [quantityOwned, setQuantityOwned] = useState(0);
const [purchaseAmount, setPurchaseAmount] = useState(10);
const productIndex = newArray.findIndex((p) => p.id === product.id);
if (productIndex !== -1) {
newArray[productIndex].quantity = newArray[productIndex].quantity + purchaseAmount
const newCost = newArray[productIndex].cost * increaseConstant;
newArray.push({
...product,
cost: newCost
});
} else {
newArray.push({
...product,
quantity: purchaseAmount,
});
}
if (productType === 'items') {
setPlayerCharacter({
...playerCharacter,
items: newArray,
});
}
// // Increase product cost
const newCost = product.cost * 1.1;
product.cost = newCost.toFixed(2);
// PPC Product
if (product.type === 'pointsPerClick') {
// Assign current values
let currentPointsPerClick = playerCharacter.pointsPerClick;
let currentTotalScore = playerCharacter.totalScore;
let newPointsPerClickValue = currentPointsPerClick + product.effect;
let newTotalScore = currentTotalScore - product.cost;
setQuantityOwned(quantityOwned + purchaseAmount); // This is the trouble
// i tried also (prev => prev + purchaseAmount)
let newTotalBuildingsOwned = playerCharacter.totalBuildingsOwned;
let newTotalItemsOwned = playerCharacter.totalItemsOwned;
if (productType === 'items') {
const newNum = playerCharacter.totalItemsOwned + purchaseAmount;
newTotalItemsOwned = newNum;
}
if (productType === 'buildings') {
const newNum =
playerCharacter.newTotalBuildingsOwned + purchaseAmount;
newTotalBuildingsOwned = newNum;
}
setPlayerCharacter({
...playerCharacter,
pointsPerClick: newPointsPerClickValue,
totalScore: newTotalScore,
totalItemsOwned: newTotalItemsOwned,
totalBuildingsOwned: newTotalBuildingsOwned,
});
}
You are concatenating the purchaseAmount (which is a string) with the current value of quantityOwned/totalItemsOwned/totalBuildingsOwned (which is a number), resulting in a string.
You need to convert the purchaseAmount to a number using Number() or parseInt() before using it in your calculation.
setQuantityOwned(prev => prev + Number(purchaseAmount));

How to render elements based on a given value in React JS?

I want to render some stars based on a specific value, and this is what I have done so far
const Rating = ({ value }) => {
const renderStars = () => {
let stars = []; // This array should contain all stars either full, half or empty star
for (let i = 0; i < value; i++) {
if (value % 1 !== 0) {
// If the value has decimal number
} else {
// If the value has NO decimal number
}
}
return stars?.map((star) => star); // Mapping the stars array
};
return <div className="rating">{renderStars()}</div>;
};
export default Rating;
Now I have 3 icons: a full star, a half star, and an empty star. Let's say the rating value is 3.5, so what I want is to push to the stars array 3 full stars 1 half star and 1 empty star so that will be 5 stars in total. And then I can map through the array and render all the stars.
You can loop through up until your value as you're currently doing, where for each iteration you push a full star, and then after the loop is complete, check if value is a decimal to determine if you should push an additional half star:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array.from({length: STAR_COUNT}, () => <EmptyStar />);
let i;
for (i = 0; i < value; i++) { // this will loop Math.floor(value) times
stars[i] = <FullStar />;
}
if (value % 1 != 0) // if value is a decimal, add a half star
stars[i-1] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
I would also suggest wrapping this component in a call to React.memo() so that the for loop logic only runs when your value prop changes, and not what the parent rerenders.
Another, perhaps more concise way, is to use some array methods to help, such as .fill() to populate an array firstly with empty stars, then replace those empty stars up to a given index based on your value, and finally add a half star if required:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array(STAR_COUNT).fill(<EmptyStar />).fill(<FullStar />, 0, Math.floor(value));
if (value % 1 != 0) // if value is a decimal, add a half star
stars[Math.floor(value)] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
Try this in your code
let rating = 3.5;
let ratingCount = 0
for(let i=1; i<=5; i++){
if(i<=rating){
console.log("full")
ratingCount++
}
}
if(rating-Math.floor(rating) !== 0){
console.log("Half")
ratingCount++
}
for(let i=ratingCount ; i<5; i++){
console.log("empty")
}

How to set value in array depending on previous value of array

I've got a simple problem, but I'm struggling to find the easiest solution without transforming the array a hundred times.
I want to do a simple stacked graph in google sheets, with weeks on X and values on Y. I got the values for each week, but only for weeks, that have a value.
The values are all calculations I've done with google apps script/ js.
person1 = [[2019/37,2], [2019/42,3]] and so on, for multiple persons and for 80 weeks in total.
The num value is the total value after each week. So I want the array to be filled up with the missing weeks. Therefore I mapped this to another array, where I have all the weeks but no values, giving these weeks the value 0:
person1= [[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
This of course does not fit to see a progress in the graph.
So I need something to set the weeks, which were filled up, to the previous value, resulting in
person1= [[2019/37,2],[2019/38,2],[2019/39,2],...,[2019/42,3],[2019/43,3],[2019/44,3],...]
Looping through this and setting the values with something like person[i][1] == person[i-1][1] seems not to be a good practice of course.
So, what would be the best way to achieve this? I'm kind of stuck with this now, I feel like I don't see the forest for the trees.
Thanks in advance!
code:
let valueArray = [[2019/37,2], [2019/42,3]]
let weeksArray = [2019/38,2019/39,2019/40,2019/41...]
//find missing weeks
let notFound = weeksArray.filter(el => valueArray.includes(el) == false).map(x => [x,0]);
//concat and sort
let outArray = arr.concat(notFound).sort((a,b)=> a[0].localeCompare(b[0]));
//output:
//[[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
Solution:
Since you already have the expanded array, you can use map on the whole array and use a function to replace the values:
var weeks = [[2019/37,2],[2019/38,0],[2019/39,0],[2019/40,3],[2019/41,0],[2019/42,4],[2019/43,0],[2019/44,0]];
weeks.map((a,b)=>{weeks[b][1] = (a[1] == 0 && b > 0) ? weeks[b-1][1] : weeks[b][1]});
To make it more readable, this is the same as:
weeks.forEach(function missing(item,index,arr) {
if (item[1] == 0 && index > 0) {
arr[index][1] = arr[index-1][1];
}
}
);
Console log:
References:
Arrow Functions
Conditional Operator
Array.prototype.map()
function fixArray() {
var array = [["2019/1", "1"], ["2019/10", "2"], ["2019/20", "3"], ["2019/30", "4"], ["2019/40", "5"]];
var oA = [];
array.forEach(function (r, i) {
oA.push(r);
let t1 = r[0].split('/');
let diff;
if (i + 1 < array.length) {
let inc = 1;
let t2 = array[i + 1][0].split('/');
if (t1[0] == t2[0] && t2[1] - t1[1] > 1) {
do {
let t3 = ['', ''];
t3[0] = t1[0] + '/' + Number(parseInt(t1[1]) + inc);
t3[1] = r[1];
diff = t2[1] - t1[1] - inc;
oA.push(t3);
inc++;
} while (diff > 1);
}
}
});
let end = "is near";
console.log(JSON.stringify(oA));
}
console.log:
[["2019/1","1"],["2019/2","1"],["2019/3","1"],["2019/4","1"],["2019/5","1"],["2019/6","1"],["2019/7","1"],["2019/8","1"],["2019/9","1"],["2019/10","2"],["2019/11","2"],["2019/12","2"],["2019/13","2"],["2019/14","2"],["2019/15","2"],["2019/16","2"],["2019/17","2"],["2019/18","2"],["2019/19","2"],["2019/20","3"],["2019/21","3"],["2019/22","3"],["2019/23","3"],["2019/24","3"],["2019/25","3"],["2019/26","3"],["2019/27","3"],["2019/28","3"],["2019/29","3"],["2019/30","4"],["2019/31","4"],["2019/32","4"],["2019/33","4"],["2019/34","4"],["2019/35","4"],["2019/36","4"],["2019/37","4"],["2019/38","4"],["2019/39","4"],["2019/40","5"]]

Compare arrays in loop - javascript

I'm doing a lottery system and I need to make sure that each Array is ​​different. This is my code:
var intNumberOfBets = 10;
let aLotteryTicket=[];
let aData = [];
for(intI = 0; intI <intNumberOfBets; intI++){
let oCasilla ={};
oCasilla.block=[];
for(intI = 0; intI <intNumberOfBets; intI++){
let oCasilla ={};
oCasilla.block=[];
Each "lottery ticket" has an array with 5 numbers. They can have the same numbers as others but in different positions.
for (let intB=1;intB<=5;intB++)
{
for(let intA=1;intA<=50; intA++){ aLotteryTicket.push(intA); }
oCasilla.block.push(aLotteryTicket.splice(parseInt(Math.random()*aLotteryTicket.length),1)); // ADD 5 NUMBERS RANDOMLY TO ARRAY
};
oCasilla.block.sort(function (a,b){ return (parseInt(a)-parseInt(b));});
aData.push(oCasilla);
alert(aData[intI].block); // show generated arrays
}//END FOR
How can I prevent each array from being the same as another, before adding it to my final Array aData[]?
Example:If i add the array 5,6,7,8,9 to oCasilla.block=[]; , i need to check that there is not another 5,6,7,8,9 in oCasilla.block=[];
Thanks in advance
You can use a set of string representations (numbers separated by comma built using join(',')) of your tickets to keep track of what was added, and only add if a ticket was not previously created.
function generateTicket() {
// generate an array with 5 unique random numbers
let a = new Set();
while (a.size !== 5) {
a.add(1 + Math.floor(Math.random() * 50));
}
return Array.from(a);
}
let oCasilla = {
block: []
};
let addedTickets = new Set(); // add stingified ticket arrays here
// add 10 unique tickets to oCasilla.block
while (oCasilla.block.length !== 10) {
const ticket = generateTicket();
if (!addedTickets.has(ticket.join(','))) {
oCasilla.block.push(ticket);
addedTickets.add(ticket.join(','));
}
}
console.log(oCasilla);

Pick random property from a Javascript object

Suppose you have a Javascript object like:
{cat: 'meow', dog: 'woof', snake: 'hiss'}
Is there a more concise way to pick a random property from the object than this long winded way I came up with:
function pickRandomProperty(obj) {
var prop, len = 0, randomPos, pos = 0;
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
len += 1;
}
}
randomPos = Math.floor(Math.random() * len);
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (pos === randomPos) {
return prop;
}
pos += 1;
}
}
}
The chosen answer will work well. However, this answer will run faster:
var randomProperty = function (obj) {
var keys = Object.keys(obj);
return obj[keys[ keys.length * Math.random() << 0]];
};
Picking a random element from a stream
function pickRandomProperty(obj) {
var result;
var count = 0;
for (var prop in obj)
if (Math.random() < 1/++count)
result = prop;
return result;
}
I didn't think any of the examples were confusing enough, so here's a really hard to read example doing the same thing.
Edit: You probably shouldn't do this unless you want your coworkers to hate you.
var animals = {
'cat': 'meow',
'dog': 'woof',
'cow': 'moo',
'sheep': 'baaah',
'bird': 'tweet'
};
// Random Key
console.log(Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]);
// Random Value
console.log(animals[Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]]);
Explanation:
// gets an array of keys in the animals object.
Object.keys(animals)
// This is a number between 0 and the length of the number of keys in the animals object
Math.floor(Math.random()*Object.keys(animals).length)
// Thus this will return a random key
// Object.keys(animals)[0], Object.keys(animals)[1], etc
Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]
// Then of course you can use the random key to get a random value
// animals['cat'], animals['dog'], animals['cow'], etc
animals[Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]]
Long hand, less confusing:
var animalArray = Object.keys(animals);
var randomNumber = Math.random();
var animalIndex = Math.floor(randomNumber * animalArray.length);
var randomKey = animalArray[animalIndex];
// This will course this will return the value of the randomKey
// instead of a fresh random value
var randomValue = animals[randomKey];
If you are capable of using libraries, you may find that Lo-Dash JS library has lots of very useful methods for such cases. In this case, go ahead and check _.sample().
(Note Lo-Dash convention is naming the library object _.
Don't forget to check installation in the same page to set it up for your project.)
_.sample([1, 2, 3, 4]);
// → 2
In your case, go ahead and use:
_.sample({
cat: 'meow',
dog: 'woof',
mouse: 'squeak'
});
// → "woof"
You can just build an array of keys while walking through the object.
var keys = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
keys.push(prop);
}
}
Then, randomly pick an element from the keys:
return keys[keys.length * Math.random() << 0];
If you're using underscore.js you can do:
_.sample(Object.keys(animals));
Extra:
If you need multiple random properties add a number:
_.sample(Object.keys(animals), 3);
If you need a new object with only those random properties:
const props = _.sample(Object.keys(animals), 3);
const newObject = _.pick(animals, (val, key) => props.indexOf(key) > -1);
You can use the following code to pick a random property from a JavaScript object:
function randomobj(obj) {
var objkeys = Object.keys(obj)
return objkeys[Math.floor(Math.random() * objkeys.length)]
}
var example = {foo:"bar",hi:"hello"}
var randomval = example[randomobj(example)] // will return to value
// do something
Another simple way to do this would be defining a function that applies Math.random() function.
This function returns a random integer that ranges from the 'min'
function getRandomArbitrary(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
Then, extract either a 'key' or a 'value' or 'both' from your Javascript object each time you supply the above function as a parameter.
var randNum = getRandomArbitrary(0, 7);
var index = randNum;
return Object.key(index); // Returns a random key
return Object.values(index); //Returns the corresponding value.
A lot of great answers here, so let me just try to spread the awareness of the bitwise NOT (~) operator in its double-trouble variant (which I'm pretty sure I learned about on StackOverflow, anways).
Typically, you'd pick a random number from one to ten like this:
Math.floor(Math.random()*10) + 1
But bitwise operation means rounding gets done faster, so the following implementation has the potential to be noticeably more performant, assuming you're doing enough truckloads of these operations:
~~(Math.random()*10) + 1

Categories

Resources