Dinero Multiply() is not a function error inside cart.totalPrice - javascript

First time I use Dinerojs. I use it inside my shoppingcart which is pure js file.
I'm including part of the code that I think is relevant here but if you need me to post more code, I will be append my question.
My shopping cart code is simple - it's a svelte store:
let cartStore = writable({
items: [],
totalPrice: 0,
totalItems : 0
})
and I add items to the shopping cart using the following code :
cartStore.update(cartStore => {
cartStore.items.push({
product: product,
totalPrice: product.price,
quantity: 1
})
To compute the item totalPrice I'm trying to multiply item.totalPrice by quantity:
updatedCartStore.items.forEach(item => {
item.totalPrice = item.product.price * item.quantity;
});
Now this code works before I added Dinerojs to my package.json and attempted to use DineroJS. When I tried to use the Dinero object, I ran into issues which I will explain shortly.
I changed my totalPrice to this:
let cartStore = writable({
items: [],
totalPrice: Dinero({amount : 0, currency : "USD"}), // >>>>changed this to include Dinero
totalItems : 0
})
and when I tried to multiply item.product.price by item.quantity...I got an error that I need to provide an integer, console.log is showing totalPrice as NAN. this is my code:
updatedCartStore.items.forEach(item => {
item.totalPrice = Dinero({ amount : item.product.price}).multiply(item.quantity);
});
I also tried the following which gave me the error "multiply is not a function" :
updatedCartStore.items.forEach(item => {
item.totalPrice = item.product.price.multiply(item.quantity);
});
So, my question is how to calculate item.totalPrice using Dinero().multiply() ? I need to multiply item.product.price by item.quantity but using Dinero.JS. What is the right way to accomplish this?
Update 1:
Mybe I added Dinero to my front wrong:
My front is this:
<ProductCard name ="dinerojs" description="to test using dinerojs" price ={Dinero({amount :40 , currency:"USD" }).toFormat()} options="false" />
Mybe toFormat() causes the number to convert to something else?

Dinero requires the "amount" to be an integer value. It tracks monetary trasactions in "cents", not "dollars" (as they call, "Minor Currency", since it supports multiple currencies)
Depending on the currency, you may need to act differently, but the default number of decimal places is 2, so as long as you don't change that, you just need to convert item.product.price to an integer:
updatedCartStore.items.forEach(item => {
item.totalPrice = Dinero({ amount : item.product.price * 100}).multiply(item.quantity);
});
Just keep in mind, if you switch to Yen or Peruvian Pesos or something, you may need to adjust things.

Related

Get initial value of Firestore document

I am trying to make an addition of a grocery quantity in my react native app. The groceries are coming from firebase firestore with pre-defined prices, I want that when I increase the quantity, the total count of the cart_items should be correctly calculated. I am approaching this by adding it directly from the server.
The issue is, I need to be able to get only the initial price of a grocery, so I can add and subtract at will, instead, I am getting the updated price when I add the quantity, and that updated price is being added to the current price when I need to increase the quantity again. I hope you get what I mean.
const increment = async (id) => {
const itemRef = doc(db, "cartItems", id);
await getDoc(itemRef).then(async (snapshot) => {
// This Line of code is supposed to capture the initial value of the price
let price = snapshot.data().data.price;
console.log(price);
// This Line of code is supposed to capture the initial value of the price
await updateDoc(itemRef, {
quantity: snapshot.data().quantity + 1,
data: {
...snapshot.data().data,
price: snapshot.data().data.price + price,
// I am supposed to use that initial value for this calculation
},
});
});
};
And here's for decreasing the quantity
const decrement = async (id) => {
const itemRef = doc(db, "cartItems", id);
await getDoc(itemRef).then(async (snapshot) => {
// This Line of code is supposed to capture the initial value of the price
let price = snapshot.data().data.price;
console.log(price);
// This Line of code is supposed to capture the initial value of the price
await updateDoc(itemRef, {
quantity:
snapshot.data().quantity === 1 ? 1 : snapshot.data().quantity - 1,
data: {
...snapshot.data().data,
price:
snapshot.data().data.price === price
? price
: snapshot.data().data.price - price,
// I am supposed to use that initial value for this calculation
},
});
});
};
So I just need to know if there's a way I can get only the initial value of the price and not the updated value. Please let me know if I need to clarify anything about the question. It's a really pressing issue for me right now.
I got my answer guys.
I just had to add an initial value that remains constant and doesn't change to my database. That's what I use to make the necessary calculations on my app.

Reducer is adding items in the store out of nowhere

So I am trying to add a cart feauture to my React-redux site and I got stuck on a very weird occurance. So this is what I get from the payload of the action for example:
{
info: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops',
id: 1,
price: 109.95,
image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg',
count: 5,
totalPrice: 549.75
}
So what Im trying to do is, when an item with the same id as this one is trying to be added, to not add it, but to increase the count of the item with the same id that already exists in the cart:
const index = state.currentCart.findIndex((x) => x.id === id);
return {
...state,
currentCart: [
...state.currentCart,
state.currentCart[index].count += 1,
(state.currentCart[index].totalPrice =
state.currentCart[index].price * state.currentCart[index].count),
],
};
The count itself is increased, but there is something really strange happening at the same time.
The total price of the product and its count are also added as elements of the currentCart array, when the only thing that should happen is to update the count of the cart item with the id from the payload,
this is what happens to the currentCart array when this action is fired:
currentCart: [
{
info: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops',
id: 1,
price: 109.95,
image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg',
count: 6,
totalPrice: 659.7
},
2,
219.9,
3,
329.85,
4,
439.8,
5,
549.75,
6,
659.7
]
}
I am sure I am not mutating the state right, thank you in advance!
No, they are not coming from nowhere, you are actively adding the values to the array.
You seem to be a bit confused about how to properly handle states. You either choose an immutable approach (which I really recommend if you are using react) or you choose to mutate your references.
In javascript, when you do an assignment, that assignment also returns the value that is being assigned, so for example here:
let x = 1
let b = x+=1
// b is now 2 and x is 2
let c = b += 2
// b is now 4 and c is also 4
That is exactly what is happening on your array assignment. You are first spreading the old version of the array on the new one (making a copy) and then you mutate the reference to the current car at the same time (and this is the key part) that you are saving the return value of those assignments in the array itself.
Take a look at the values on the array, they are the results of your operations:
count (1) += 1 // 2
price (109.95) * count (2) = 219.9,
count (2) += 1 // 3
price (109.95) * count (3) = 329.85
... etc
So what you have on your array is an historic of the count and total price values.
This is a breakdown of what is happening in your code:
// Will allways be at index 0, because you add it as first element
// and then you keep copying the array below
const index = state.currentCart.findIndex((x) => x.id === id);
return {
...state,
currentCart: [
// Here you are copying the old array into the new one,
// keeping the current car at the first position
...state.currentCart,
// Here you are updating the values of the object at index 0
// and at the same time you are adding those values at
// the end of the array
state.currentCart[index].count += 1,
(state.currentCart[index].totalPrice =
state.currentCart[index].price * state.currentCart[index].count),
],
};
What you want to do is to build a new currentCart each time and. Also you want to use an object for currentCart, not an array. If you want to keep a list of items in the cart, I suggest you tu create a nested property on the cart called items, and make that be an array.
Your code example is not showing us where are you getting the action from, but I will provide you an example assuming you just have it and that the new item to add to the cart comes in the payload.
const currentCart = state.currentCart;
const newItem = action.payload
return {
...state,
currentCart: {
...currentCart,
count: currentCart.count + 1
totalPrice: (newItem.price * newItem.count) + currentCart.totalPrice,
items: [...currentCart.items, newItem]
},
};
I am not sure but this is happening
totalPrice = item's price * no of times the item is added
Other items' price is not getting included. Try this -
state.currentCart[index].totalPrice += state.currentCart[index].price * state.currentCart[index].count
(just '+=' instead of '=')

Add Sum total of Products in a Cart

I have a rookie problem, and that is adding the sum total from an object (Cart) in my cart page before I go check out.
Every time I navigate from one screen to the other the amount keeps going up
I want the total amount to be 159, or the correct amount if i add more products
SourceCode
Try to replace this piece of code:
this.cartItems.forEach((value, index) => {
this.totalAmount += parseInt(value.amount);
});
with this:
this.totalAmount = this.cartItems.reduce((acc, item) => {
return acc += item.amount;
}, 0);
In the first case you add a new value to already existing value. And in the reduce version it should rewrite the totalAmount.
Complete working example find out here in this StackBlitz Link
You just need to calculate cart amount using reduce() array function.
this.total = this.cart.reduce( (acc,curVal) => {
return acc + (curVal.amount * curVal.quantity);
//this.temp.push( curVal.amount * curVal.quantity);
},0)

Console logging object values

As part of a bootcamp I am taking, I have been asked to create an online shopping cart.
I am having such a problem with the addToCart function in the sense that I keep managing to to console log [{itemName: item, itemPrice: 8}] rather than 'item has been added to cart.
I think my problem lies in my template literal.
The aim is for the function to take in an item, create an object around it, apply additional properties, add to the cart array, console log that the item has been added (this is where my problem lies) then return the cart.
I have tried a number of alternatives such as trying to access the values via dot notation and using the key of the object to access the value. still have had no joy.
Below is my code:
function addToCart(item) {
// write your code here
var price = Math.floor(Math.random() * 100) + 1;
var obj = {itemName: item, itemPrice: price};
cart.push(obj);
console.log(`${item} has been added to your cart.`)
return cart;
}
What I am expecting is for the item that is being parsed into the function to be log to the console stating that the item has been added. I believe the rest of the code is working fine as it is passing the local tests.
The error I get is this:
Error: Expected [{itemName:'ice cream', itemPrice: 26}] to equal 'ice cream has been added to your cart.'
Any tips would be very helpful.
I had a look at your tests,
Try following
your test is (I hope I picked the right one) :
it("returns a message indicating that the item has been added", function() {
expect(addToCart("ice cream")).toEqual("ice cream has been added to your cart.");
expect(addToCart("juice")).toEqual("juice has been added to your cart.");
});
Change your code to :
function addToCart(item) {
// write your code here
var price = Math.floor(Math.random() * 100) + 1;
var obj = {itemName: item, itemPrice: price};
cart.push(obj);
console.log(`${item} has been added to your cart.`)
return `${item} has been added to your cart.`;
//OR
//return `${obj.itemName} has been added to your cart.`;
}
As you are comparing the returned value from "addToCart" in your test with the string in .toEqual so, so you should return that particular formatted string from your function.
So instead of returning cart, you should return that particular string that you want to check.
You need to define the cart as a global variable. Please check the below code snippet.
cart = [];
(function() {
addToCart("Apple");
addToCart("Orange");
console.log(this.cart)
})();
function addToCart(item) {
var price = Math.floor(Math.random() * 100) + 1;
var obj = {itemName: item, itemPrice: price};
this.cart.push(obj);
console.log(`${item} has been added to your cart.`)
return cart;
}
Looking at the error you're getting makes me think the error is thrown by a test you're running.
That test is checking the value returned by your function and not what's logged in the console. In order to not get the error anymore, you should fix that test.
Assuming your tests are using Jasmine, that test would look something like:
const cart = addToCart(item);
expect( cart[0]['itemName'] ).toBe('ice cream')

Aggregate multiple fields in one rethinkdb query

How to reduce / aggregate multiple fields in a table? This does not seem efficient:
r.object(
'favorite_count',
r.db('twitterdb').table('tweets').map(tweet => tweet('favorite_count')).avg().round(),
'retweet_count',
r.db('twitterdb').table('tweets').map(tweet => tweet('retweet_count')).avg().round()
)
(Expected) Result:
{
"favorite_count": 17 ,
"retweet_count": 156
}
I'm not sure if it's possible to make RethinkDB work like you want in one go using its built-ins, but you can implement the avg function yourself really easy:
r.db('twitterdb')
.table('tweets')
.fold(
{_: 0, favorite_count: 0, retweet_count: 0},
(a, tweet) => ({
_: a('_').add(1),
favorite_count: a('favorite_count').add(tweet('favorite_count')),
retweet_count: a('retweet_count').add(tweet('retweet_count'))
})
)
.do(a => ({
favorite_count: r.branch(a('_').gt(0), a('favorite_count').div(a('_')).round(), null),
retweet_count: r.branch(a('_').gt(0), a('retweet_count').div(a('_')).round(), null)
}))
I have quickly-tested the above over a small set of data, and enabling the query profiling showed at least /2 shard accesses and less time to execute.
However I'm not sure about the overall profiler output and I don't think I can interpret its details (I believe that native avg is more optimized, but it looks cheaper that accesing the data at least in two rounds).
Additionally, this custom avg function implementation is more 0-elements friendly not throwing an error.
If the length of the array is known (e.g. 7430), this is faster:
r.db('twitterdb').table('tweets')
.reduce((agg, item) => {
return {
favorite_count: agg('favorite_count').add(item('favorite_count')),
retweet_count: agg('retweet_count').add(item('retweet_count'))
}
})
.do(result => r.object('favorite_count', result('favorite_count').div(7430).round(), 'retweet_count', result('retweet_count').div(7430).round()))

Categories