NaN issues on my sum of values in my array of objects - javascript

i have been building this APP in VUE.js , but have a little issue on one function where in im trying to add the values of each object inside the array..throwing me a NaN error.Lets say
script
data(){
return{
array:[
{ product_name: "Chain Saw",
product_free: 2,
product_free_discount: 306.8,
product_id: 1,
}
{ product_name: "Ox",
product_free: 1,
product_free_discount: 60.8,
product_id: 1,
}
],
totalDiscEquals:0,
}
}
then on my computed property i just set this function
COMPUTED
totalDiscObUnique() {
let total = this.array.reduce(
(a, b) => a + b.product_free_discount,
0
);
console.log(total);
return this.totalDiscEquals=total;
},
creating the process in the created module like this
created(){
this.totalDiscObUnique;
but the when i console log the value of totalDisEquals, throws me a NaN result , any idea about why is this happening, or sugerences about why NaN mostly times ocurr for?

new Vue({
el: '#app',
data: {
array:[
{
product_name: "Chain Saw",
product_free: 2,
product_free_discount: 306.8,
product_id: 1,
}, // <--- [1] missing comma here (,)
{
product_name: "Ox",
product_free: 1,
product_free_discount: 60.8,
product_id: 1,
}
],
totalDiscEquals:0,
},
/* [2] It's not recommended to assign new value to a data property
in a computed, instead it intended to give you just a filtered / reduced
/ ... response. */
computed: {
totalDiscObUnique() {
return this.array.reduce((a, b) => a + b.product_free_discount, 0);
},
},
created () {
/* [3] also, there is no need to reassign the same value to a new
data property again, instead you can use the computed itself wherever you
want to use it */
// this.totalDiscEquals = this.totalDiscObUnique
console.log(this.totalDiscObUnique)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{ totalDiscObUnique }}
</div>

Related

How to do projection in TypeScript?

I want to populate orders which is an array of type Order. The expected result is orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}]. How to do so in TypeScript? I am new to it.
export class Product {
constructor(public id: number, public name: string, public price: number) {}
}
export interface Order {
id: number;
qt: number;
}
export const products: Product[] = [
new Product(1, 'Apple', 2.1),
new Product(2, 'Banana', 2.2),
new Product(3, 'Chocolate', 2.3),
new Product(4, 'Dessert', 2.4),
];
export const cart: Product[] = [
products[0],
products[0],
products[2],
products[1],
products[2],
products[0],
products[1],
products[0],
];
export const orders: Order[] = [];
Edit
For those who want to know how
orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}] is obtained.
In the cart:
the quantity of apples (id:1) is qt:4
the quantity of bananas (id:2) is qt:2
the quantity of chocolates (id:3) is qt:2
So by using cart, I have to obtain orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}]. It should be clear.
Since you're looking for a "LINQ-like" solution, you probably want to use the higher order functions like map/filter/reduce.
Strictly, your problem cannot be solved purely with LINQ projections. Those merely represent map (Select), concatMap/flatMap (SelectMany), and zip (Zip). Your problem involves counting the occurences of each id throughout the entire array.
Pretty much every data manipulation problem can be solved with higher order folds, i.e reduce in javascript land, Aggregate in C# land. This one is no exception. The first thing to do, is to count the occurrences of each id, and build a counter object.
cart.reduce((acc, { id }) => {
acc[id] = (acc[id] ?? 0) + 1;
return acc;
}, {} as Record<number, number>);
Essentially, you start the fold operation with an empty object, then add each id and its occurrence count. Every time an id is encountered in the cart array, you increment its count in the object. If the id doesn't already exist in the accumulating object, nullish coalescing (acc[id] ?? 0) uses 0 and increments that instead.
This will give you-
{ '1': 4, '2': 2, '3': 2 }
Now, you need to turn this into-
[ { id: 1, qt: 4 }, { id: 2, qt: 2 }, { id: 3, qt: 2 } ]
For that, use Object.entries on the fold result to get-
> Object.entries({ '1': 4, '2': 2, '3': 2 })
[ [ '1', 4 ], [ '2', 2 ], [ '3', 2 ] ]
Finally, a simple map is all you need-
Object.entries(...).map(([id, qt]) => ({ id: Number(id), qt }))
Combining all that, you have-
export const orders: Order[] = Object.entries(
cart.reduce((acc, { id }) => {
acc[id] = (acc[id] ?? 0) + 1;
return acc;
}, {} as Record<number, number>)
).map(([id, qt]) => ({ id: Number(id), qt }));
One thing to note here is that Object.entries is pretty inefficient since it builds up an array instead of an iterator. If you're into efficiency, roll an iterator version of Object.entries and use that instead, using generator functions-
function* objEntries<T>(x: Record<string, T>): IterableIterator<[string, T]> {
for (const k in x) {
yield [k, x[k]];
}
}

Reduce function wrapping data in an array

I have a reduce function that formats my data in the way i need but the only issue is the data is nested inside an array. I need to remove the outter array or just stop the reduce function from adding it in but every attempt Ive made to stop the reducer from wrapping the data in an array breaks my code. Ideally I would like my reducer to not wrap the data in an array but if thats not possible removing the array i need from inside the reducer cleanly seems like the only solution:
my data looks like this:
{
count: 4,
datapoints: [
{
Date: "2021-05-05",
score: 0,
},
{
Date: "2021-05-12",
score: 0,
},
{
Date: "2021-05-30",
score: 0,
},
{
Date: "2021-06-03",
score: 114,
},
],
};
my reducer function and api call:
const riskScores = await api.PCC.riskAssessment(userID, startdate, endDate);
const riskScoresFormatted = riskScores.datapoints.reduce((result, data) => {
const scores = result["riskAssessment"] || [];
scores.push({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
});
result["riskAssessment"] = scores;
return result;
}, []);
the output:
[riskAssessment: [{…}, {…}, {…}, {…}] ]
Ive tried just using the index of riskScoresFormatted[0] that comes back undefined. riskScoresFormatted.slice(1) just returns an empty array. Ive also tried targeting the first Item like riskScoresFormatted.riskAssessment this works but the value is sometimes null so it causes bugs later down the line.
Try changing the final reduce argument from [] to {} and I think you'll have better luck.
const riskScoresFormatted = riskScores.datapoints.reduce((result, data) => {
const scores = result["riskAssessment"] || [];
scores.push({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
});
result["riskAssessment"] = scores;
return result;
}, {});
Or, use Array.map() instead:
const riskScores = {
count: 4,
datapoints: [{
Date: "2021-05-05",
score: 0,
},
{
Date: "2021-05-12",
score: 0,
},
{
Date: "2021-05-30",
score: 0,
},
{
Date: "2021-06-03",
score: 114,
},
],
};
var riskScoresFormatted = riskScores.datapoints.map((data) => ({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
}));
console.log(riskScoresFormatted);

Converting Array to Object for selecting objects (Refactoring / Optimizing)

While I was facing slow loading time when it iterate array to render objects, I want to change its data structure. I show table of contents for seasons. When user clicks an item, the item is marked as selected.
Here is current data structure (Array)
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
I'm storing selected Season Ids as an Array now
state = {selectedSeasonIds: []}
When selectedSeasonIds has id, I want to remove the id from it. Otherwise, add the id to selectedSeasonIds. (This is current approach)
if(_.includes(this.state.selectedSeasonIds, id)) {
let newSelectedSeasonIds = _.filter(this.state.selectedSeasonIds, (curObject) => {
return curObject !== id;
});
this.setState({selectedSeasonIds : newSelectedSeasonIds});
} else {
let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
this.setState({selectedSeasonIds : newSelectedSeasonIds});
}
And here is my pseudo-code for refactoring to convert my arrays to object structure for performance. (I found searching on an object is MUCH faster than searching on the array)
Changing the array to object
const seasons = {
6 :{
id: 6,
value: 'All',
},
7: {
id: 7,
value: 'Spring',
},
8: {
id: 8,
value: 'Summer',
},
9: {
id: 9,
value: 'Fall',
},
10: {
id: 10,
value: 'Winter',
}
};
Changing Selected Seasons <- I want to store only the key(id) of the objects. But I want to use it as an object
state = {selectedSeasonIds : {}} Can I store object type state?
Here is expected logic which can be 50 times faster than array search.
if(selectedSeasonIds[id]) {
//remove
return _.omit(state.selectedSeasonIds, id); < is this right?
} else {
//add
return {...state.selectedSeasonIds, [id]:id} <- Does this look ok?
}
Well if you think this is right, you can copy and paste my code to the answer (I will edit my question too).
Otherwise, Can you provide better suggestion or find the error?
Thank you so much
I guess you have to loop through seasons in order to render them.
My first suggestion is to add selected prop in each one of them so you don't have to check in selectedSeasonsIds on every render.
In case this is not an option, you can still keep the key value approach.
onAdd(id) {
this.setState({
selectedSeasonsIds: {
...this.state.selectedSeasonsIds,
[id]: this.state.selectedSeasonsIds[id] ? false : true
}
})
}
When checking for specific season whether they are selected or not, simply:
render() {
const { seasons, selectedSeasonsIds } = this.state
return (
<div>
...
{Object.keys(seasons).map(key =>
<ItemComponent
{...propsThatYouMightNeed}
selected={selectedSeasonsIds[key]}
/>
)}
</div>
)
}
Maybe something like this? I'd recommend storing arrays and then converting as necessary for lookups.
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
const seasonsHash = _.keyBy(seasons, 'id');
// check for existence
const hasId = _.has(seasonsHash, id)
// remove and convert back to array
_.values(_.omit(seasonsHash, id))
// add new id
_.concat(_.values(seasonsHash), id)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

VueJs v-for loop not updating correctly

Im trying to make a form where you have buttons to add children inputs but after adding one child the button no longer works and it returns to error in the cole as to why but if i output the data it looks like it is being adding just the DOM isn't being updated with the new input box
var default_item = {
value: 0,
reps: 0,
sets: 0
}
var deafult_exercise = {
exercise_name: '',
item_data: [default_item]
};
new Vue({
el: '#app',
data: {
log_data: [
{
log_name: 'log #1',
log_day: 1,
exercise_data: [
{
exercise_name: 'exercise #1',
item_data: [
{
value: 50,
reps: 5,
sets: 5
}
]
}
]
}
]
},
methods: {
addLog: function(){
this.log_data.push({log_name: '', log_day:0, exercise_data: deafult_exercise});
},
addExercise: function(index_id) {
this.log_data[index_id].exercise_data.push(deafult_exercise);
},
addItem: function(index_id, log_id) {
this.log_data[log_id].exercise_data[index_id].item_data.push(default_item);
}
}
})
JSfiddle: https://jsfiddle.net/renlok/5mpace1q/3/
Your issue is that you are pushing the same items and referencing the same items so essentially you are only ever adding 1 item
You need to use
Object.assign({}, default_item)
To use new instances of the items see updated fiddle https://jsfiddle.net/5mpace1q/4/

Rethinkdb append to array if exists

In RethinkDB, I have a table authors with the following layout:
{
id: 12,
videos: [1,2,3]
}
Now I get new authors with objects like this:
{
id: 12,
videos: [4,5]
}
If the author now already exists, I want to append the new videos 4 and 5 to the list of videos.
If author not exists, just insert the document like it is.
My approach was the following, but it didn't work.
r.table('authors').getAll(4, 3, 2, 1, {index: 'id'})
.replace(function(author, index) {
return r.branch(
author.eq(null),
{
id: index,
videos: [1,2,3]
},
author
);
})
-> Response:
{
"deleted": 0 ,
"errors": 3 ,
"first_error": "Expected 2 arguments but found 1." ,
"inserted": 0 ,
"replaced": 0 ,
"skipped": 0 ,
"unchanged": 0
}
Thanks!
Your logic is very good. It is just some syntax issue.
Given an author, if the author isn't existed, insert it, otherwise, append the video array, here is what I think of, using your logic:
var author = {
id: 12,
videos: [9, 10]
};
r.table('authors').insert(author).do(
function (doc) {
return r.branch(doc('inserted').ne(0),
r.expr({inserted: 1}),
r.table('authors').get(author["id"]).update(function(doc) {
return {videos: doc('videos').union(author["videos"])}
})
)
}
)
If the insert is sucesfully, it means we have no document with the same id, we don't have to do anything. Otherwise, we will update the document and append the videos into it.
To updare an array of multiple author, we can use foreach and expr to turn array into ReQL object. However, in this case, we use bracket to get field instead of using [] as in JavaScript object
var authors = [{
id: 12,
videos: [90, 91]
},{
id: 14,
videos: [1, 2]
}];
r.expr(authors).forEach(function(author) {
return r.table('authors').insert(author).do(
function (doc) {
return r.branch(doc('inserted').ne(0),
r.expr({inserted: 1}),
r.table('authors').get(author("id")).update(function(doc) {
return {videos: doc('videos').union(author("videos"))}
})
)
}
)
})
Something like this should do it:
r([4, 3, 2, 1]).foreach(function(id) {
return r.table('authors').get(id).replace(function(row) {
return r.branch(row.eq(null), {id: id, videos: [1, 2, 3]}, row);
});
});
lambda for replace method has only 1 argument. But you are trying too pass 2 args: author, index.

Categories

Resources