Vue computed property overwriting global state without vuex - javascript

I have a list of people who have scores. In state I have them listed in an array, one of the items in the array is 'scoreHistory' which is an array of objects containing their scores at different points in time. I want to filter this set for different time periods i.e. -5 days, -30 days so instead of just seeing the overall score I can see the scores if everyone started at 0 say 30 days ago.
I have it (kind of) working. See my code below:
filteredScores () {
if(!this.people) {
return
}
// Here I was trying to ensure there was a copy of the array created in order to not change the original array. I thought that might have been the problem.
let allPeople = this.people.slice(0) // this.people comes from another computed property with a simple getter. Returns an array.
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
for (const p of allPeople ) {
let filteredScores = inf.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
p.scoreHistory=filteredScores
//calculate new score
p.score = inf.scoreHistory.reduce(function(sum,item) {
return sum + item.voteScore
},0)
}
return allInf
}
I expected it to return to me a new array where each person's score is summed up over the designated time period. It seems to do that OK. The problem is that it is altering the state that this.people reads from which is the overall data set. So once it filters all that data is gone. I don't know how I am altering global state without using vuex??
Any help is greatly appreciated.

Your problem isn't that you're modifying the array, but that you're modifying the objects within the array. You change the scoreHistory and score property of each item in the array. What you want to do instead is create a new array (I recommend using map) where each item is a copy of the existing item plus a new score property.
filteredScores () {
if(!this.people) {
return
}
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
return this.people.map(p => {
let filteredScores = p.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
//calculate new score
let score = filteredScores.reduce(function(sum, item) {
return sum + item.voteScore
}, 0)
// Create a new object containing all the properties of p and adding score
return {
...p,
score
}
}
})

Related

Adding onto float within json object

So I have a JSON object that is dynamically creates based on lots. I'm trying to get the total cost of each lot. Each object in the list is a different purchase.
var lot_list = [{lot:123456,cost:'$4,500.00'}, {lot:654321, cost:'$1,600.00'}, {lot:123456, cost:'$6,500.00'}]
I want the total cost for each lot so I tried
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
totalBalances[lots[lot]] += parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
This ends with every lot having a null cost
I also tried
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
totalBalances[lots[lot]] = parseInt(totalBalances[lots[lot]]) + parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
Neither of these worked any help is much appreciated.
You cannot get a value to sum with parseFloat('$4,500.00') because of the invalid characters. To remove the dollar sign and commas you can replace using;
> '$4,500.00'.replace(/[^\d\./g,'')
> "4500.00"
Here the regex matches anything that is not a digit or decimal place with the global modifier to replace all occurrances.
You can map your array of objects to the float values using Array.map() to get an array of float values.
> lot_list.map((l) => parseFloat(l.cost.replace(/[^\d\.]/g,'')))
> [4500, 1600, 6500]
With an array of float values you can use Array.reduce() to get the sum;
> lot_list.map((l) => parseFloat(l.cost.replace(/[^\d\.]/g,''))).reduce((a,c) => a+c)
> 12600
EDIT
To get totals for each lot map an object with the lot id included and then reduce onto an object with;
> lot_list
.map((l) => {
return {
id: l.lot,
cost: parseFloat(l.cost.replace(/[^\d\.]/g,''))
}
})
.reduce((a,c) => {
a[c.id] = a[c.id] || 0;
a[c.id] += c.cost;
return a;
}, {})
> { 123456: 11000, 654321: 1600 }
Here the reduce function creates an object as the accumulator and then initialises the sum to zero if the lot id has not been summed before.
based on your code
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
// totalBalances[123456] is not defined yet, this should gets you problem if you're trying calculate its value
// totalBalances[lots[lot]] += parseFloat(lots['cost'].replace('$','').replace(',',''));
// maybe you should use this instead
totalBalances[lots[lot]] = parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
but, if you want to count total value of cost, you might considering using array reduce
function countCost(){
return lot_list.reduce((accum, dt) => {
return accum + parseFloat(l.cost.replace(/[^\d\.]/g,''))
}, parseFloat(0));
} //this gonna bring you total count of all cost

How to pass an array to a function without mutating the original array?

I'm working on a helper function within a bigger function.
I have an array of team object (each team looks like this {team:unique number of the team, points:0, goalDiff:0, goalsFor:0}) as the first argument of my function and I have an array of game results as a second argument (each game looks like this [number ref home team, number ref away team, goals scored by home team, goals scored by away team]).
My function looks at each game in the second array and allocate 2 points to the winning team and zero to the losing one. in case of draw, it allocates 1 point to each team. Function also counts the goals scored by each team.
let computeGames = (arr, games) => {
games.forEach((game) => {
let homeTeamIndex = arr.findIndex(team => team.team === game[0])
let awayTeamIndex = arr.findIndex(team => team.team === game[1])
let homeTeam = arr[homeTeamIndex]
let awayTeam = arr[awayTeamIndex]
game[2] > game[3]
? (homeTeam.points += 2)
: game[2] === game[3]
? ((homeTeam.points += 1), (awayTeam.points += 1))
: (awayTeam.points += 2);
homeTeam.goalsFor += game[2];
awayTeam.goalsFor += game[3];
homeTeam.goalDiff += game[2] - game[3];
awayTeam.goalDiff += game[3] - game[2];
});
return arr
};
My code is counting the points and goals as expected, but my issue is that when I execute something like the code below, the points are being counted in my teams array as well as in my doNotMutate array.
I do not understand why teams is being mutated given it's a copy of teams that is passed to the function, not the teams array itself. If someone could explain, I'm getting really confused here.
Cheers
const teams = createTeams(number) // this is a helper function to generate my teams with all values = 0
let doNotMutate = [...teams]
doNotMutate= computeGames(doNotMutate,games)
console.log(teams, doNotMutate)
Doing [...teams] does not copy the objects stored within the array. Each item of the array is simply copied into a new array. In your case each item is an object, meaning the new array will contain the exact same objects (think of them as "references to the same value" if that helps).
You can observe this:
teams[0] === doNotMutate[0] // true! (we don't want this)
You'll want to step through the array and map each item to a copy of itself ({...obj}). Something like this:
const doNotMutate = [...teams].map(game => ({...game}));
Alternatively, explore deep-clone implementations such as lodash's.

I want to get the average my firebase data

I want to average the related values ​​when the data in the FireBase is updated.
I am using Firebase functions and can not load data.
I can change the data I want when the event occurs, but I can not calculate the average of the data.
exports.taverage = functions.database.ref('/User/tsetUser/monthQuit/{pushId}')
.onCreate((snapshot, context) => {
const promiseRoomUserList = admin.database().ref('/User/tsetUser/monthQuit/{pushId}').once('value');
var sum=0;
const arrayTime = [];
snapshot.forEach(snapshot => {
arrayTime.push('/User/tsetUser/monthQuit/{pushId}'.val());
})
for(let i=0; i<arrayTime.length; i++){
sum+=arrayTime[i];
}
return admin.database().ref('/User/tsetUser/inform/standardQuit').set(sum);
});
//I Want 'standardQuit' value set average.
I'm not sure why you can't calculate the average, but a simpler version of your code would be:
exports.taverage = functions.database.ref('/User/tsetUser/monthQuit/{pushId}')
.onCreate((snapshot, context) => {
return admin.database().ref('/User/tsetUser/monthQuit/{pushId}').once('value')
.then(function(snapshot) {
let sum=0;
snapshot.forEach(child => {
sum = sum + child.val();
})
let avg = sum / snapshot.numChildren();
return admin.database().ref('/User/tsetUser/inform/standardQuit').set(avg);
});
});
The biggest differences:
This code returns promises from both the top-level, and the nested then(). This is needed so Cloud Functions knows when your code is done, and it can thus stop billing you (and potentially shut down the container).
We simply add the value of each child to the sum, since you weren't using the array in any other way. Note that the child.val() depends on your data structure, which you didn't share. So if it fails there, you'll need to update how you get the exact value (or share you data structure with us).
The code actually calculates the average by dividing the sum by the number of child nodes.
Consider using a moving average
One thing to keep in mind is that you're now reading all nodes every time one node gets added. This operation will get more and more expensive as nodes are added. Consider if you can use a moving average, which wouldn't require all child nodes, but merely the current average and the new child node. The value will be an approximate average where more recent value typically have more weight, and is much cheaper to calculate:
exports.taverage = functions.database.ref('/User/tsetUser/monthQuit/{pushId}')
.onCreate((snapshot, context) => {
return admin.database().ref('/User/tsetUser/inform/standardQuit').transaction(function(avg) {
if (!avg) avg = 0;
return (15.0 * avg + snapshot.val()) / 16.0;
});
});

Delete Session Storage - Shopping Cart Project

I hope this is a good question. I am working on a shopping cart project. I have been scouring the internet through different tutorials on shopping carts. I am attempting to write mine in Vanilla Javascript. I am having a problem with removing shopping cart items from session storage.
Below is what is currently in my session storage. As you can see it is an Array of objects.
[{"id":"8","name":"Candy
Skull","price":"20000","image":"../images/candyskull-
min.JPG","qty":"1"},{"id":"5","name":"Upsidedown
House","price":"20000","image":"../images/upsidedownhouse-
min.JPG","qty":"1"},{"id":"6","name":"Brooklyn
Window","price":"30000","image":"../images/brooklynwindow-
min.JPG","qty":"1"},{"id":"4","name":"Hand That
Feeds","price":"40000","image":"../images/handthatfeeds-
min.JPG","qty":"1"}]
I want to loop through the array and remove the matching object from the cart storage.
Below is the JS Code used to generate the .remove-from-cart buttons. As you can see it includes all the dataset information.
<td>
<span class="remove-from-cart">
<b data-id="${value.id}" data-name="${value.name}" data-
price="${value.price}" data-image="${value.image}" data-
qty="${value.qty}">X</b>
</span>
</td>
To test the functionality of what I have done so far you can visit www.dancruzstudio.com/shop
The function that I can't get to work properly is the removeFromStorage() function. For some reason when comparing an object to the objects in the array I'm never getting back a true boolean value, even when there are items in the cart that should match. Where am I going wrong? I hope someone can help. Below is a copy of my JS code.
The method I am using is having an identical dataset value in the remove item button generated by JS and then parsing that dataset into an object and comparing it to the objects in the session storage array which is called shopItems inside the removeFromStorage() function. I hope this information is enough for someone to see my problem. Thank you in advance.
// Remove item from DOM
document.querySelector('#cart-list').addEventListener('click',
removeFromCart)
function removeFromCart(e) {
if(e.target.parentElement.classList.contains('remove-from-cart')) {
//Remove from DOM
e.target.parentElement.parentElement.parentElement.remove();
//Remove from Session Storage
removeFromStorage(e.target.dataset);
}
}
// remove from Session storage
function removeFromStorage(removedItem){
let shopItems;
if(sessionStorage['sc'] == null){
shopItems = [];
} else {
shopItems = JSON.parse(sessionStorage['sc'].toString());
}
var compare = JSON.parse(JSON.stringify(removedItem))
shopItems.forEach(function(item, index){
if(compare === item){
console.log(compare);
console.log(item);
// shopItems.splice(index, 1);
}
});
sessionStorage['sc'] = JSON.stringify(shopItems);
}
You can not compare objects like this.
let a = {p:1};
let b = {p:1};
console.log(`a ===b ? ${a===b}`);
If your objects are fairly simple you can try comparing their stringify representation:
let a = {p:1};
let b = {p:1};
const compare = (x,y) => {
return JSON.stringify(x) === JSON.stringify(y);
}
console.log(`a === b ? ${compare(a,b)}`);
or write your custom compare function (that may be challenging):
Compare JavaScript objects
Since your objects are decorated with an id, the easisest way would be to idntify them by that:
let storage = [{"id":"8","name":"Candy Skull", "price":"20000","image":"../images/candyskull- min.JPG","qty":"1"},{"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"},{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"},{"id":"4","name":"Hand That Feeds","price":"40000","image":"../images/handthatfeeds- min.JPG","qty":"1"}];
let items = [{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"}, {"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"}];
const pluck = (acc, crt) => {
acc.push(crt.id);
return acc;
};
let storageIndexes = storage.reduce(pluck, []);
let itemsIndexes = items.reduce(pluck, []);
let removeIndexes = [];
itemsIndexes.forEach(id => removeIndexes.push(storageIndexes.indexOf(id)));
console.log('storage', storage);
console.log('removed items', items);
removeIndexes.sort().reverse().forEach(index => storage.splice(index,1));
console.log('remaining storage', storage);

Why does map work differently when I return an array instead of some primitive in the callback function?

Script
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp=companies.map(company=>{company.start+10;return company});
console.log("mapped company function");
console.log(mappingComp.forEach(company=>console.log(company)));
In the above snippet there is no change in start field of companies array . Why ?
In case I do below I do get modified values for start field from companies array.
var mappingComp=companies.map(company=>company.start+10);
You aren't assigning the result of company.start+10 to anything - it's just an orphaned expression.
var mappingComp = companies.map(company => {
company.start + 10;
return company
});
is just like
var mappingComp = companies.map(company => {
33;
return company
});
The expression is evaluated to a value and then discarded. If you want to add 10 to company.start, use += or =:
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp = companies.map(company => {
company.start += 10;
return company;
});
console.log(mappingComp);
But this will mutate the original array, which is (often) not a great idea when using map. If you don't want to change the original array, map to a new object:
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp = companies.map(({ start, ...rest }) => ({
start: start + 10,
...rest
}));
console.log(mappingComp);
company.start + 10 is a simple expression. It's not an assignment statement, that you are expecting it to be. And you are returning the initial array company so it makes sense that it will be returned unaltered.
when you tried the single line fat arrow function with the map. What happens is that you created another entirely different array of mutated values. The array created was populated with values (company.start +10) and returned. Note: This actually didn't change the initial array ie company.
Read up on fat arrow functions, map, filter.

Categories

Resources