Check arrays to find equal id - javascript

I have a list of products like:
listOfProducts = [{name: "prod1", productId: 1}, {name:"prod2", productId: 2},.....]
If I choose a product i'll populate the array
listSelectedProducts
it will contains the choosen products.
And I make a post to save my choise.
Now If I go back in the products page, i'll have an array:
oldProductsArray
which contains the products that I have saved in db.
listSelectedProducts
with my selected products.
Now If I remove the selected product to choose another one, my oldProductsArray will have the first product, but listSelectedProducts will have the new product choose.
So now I should remove from my db the product that I don't want anymore (an it is in oldProductsArray). So I thought to compare (and there I have my problem) the two arrays and if elements in oldProductsArray are not in listSelectedProducts, i'll create a new array with the products not selected to delete them.
So let's do an example:
I'm in my products page and choose a product.
So listSelectedProducts = [{name: "prod1", productId: 1}]
and I will post in db.
I return in products page and this time I have:
listSelectedProducts = [{name: "prod1", productId: 1}]
oldProductsArray = [{name: "prod1", productId: 1}]
I deselect the product: prod1 and choose the product: prod2
So I have:
listSelectedProducts = [{name: "prod2", productId: 2}]
oldProductsArray = [{name: "prod1", productId: 1}]
and now I should check if products in oldProductsArray are also in listSelectedProducts, if they are I can do the post, if they are not (like in this case) I should remove from db the product.
So I have a function:
this.checkOldProduct(oldProductsArray, listSelectedProducts)
in this function:
checkOldProduct(oldProductsArray, listSelectedProducts){
let arrayProductToDelete = []
// i have tried like this, but it doesn't work properly.
listSelectedProducts.filter(p1 => !oldProducts.some(p2 => { p1.productId === p2.productId, arrayProductToDelete.push(p2) }));
}
I hope I have expressed myself well, in any case I am ready to add clarifications. thank you

some quick consideration first:
If that's really all the data you have for the product list, you should probably just use a PUT method on your API and simply replace the whole list without having to compute the difference
If you still somehow need to make separate operations to update products list, I guess you should make POST requests to add new items, DELETE requests to delete items and PATCH requests to update single items (like same id but different quantities?)
From the point above: do you also have quantities for items?
Question Specific Answer
So based solely on your question I think easiest way is to find the list of items to delete something like this:
const removedItems = oldArray.filter((old) => !newArray.find((_new) => old.id === _new.id));
And request their deletion.
Full Diff Answer
If you want to compute a full diff of your chart items so you can make multiple update requests, you could do something like this:
function arrayDiff(oldArray, newArray) {
const addedAndUpdatedItems = newArray.reduce((diff, item, index, array) => {
const oldItem = oldArray.find((old) => old.id === item.id);
if(!oldItem) {
diff.added.push(item);
} else if(oldItem.quantity !== item.quantity) {
diff.updated.push(item);
}
return diff;
}, {
added: [],
updated: []
});
const removedItems = oldArray.filter((old) => !newArray.find((_new) => old.id === _new.id));
return {
...addedAndUpdatedItems,
removed: removedItems
}
}
const oldArray = [{ name: "prod1", id: 1, quantity: 1 }, { name: "prod3", id: 3, quantity: 4 }];
const newArray = [{ name: "prod1", id: 1, quantity: 3 }, { name: "prod2", id: 2, quantity: 3 }];
const diff = arrayDiff(oldArray, newArray);
console.log({ diff });
Output is
{
"added": [
{
"name": "prod2",
"id": 2,
"quantity": 3
}
],
"updated": [
{
"name": "prod1",
"id": 1,
"quantity": 3
}
],
"removed": [
{
"name": "prod3",
"id": 3,
"quantity": 4
}
]
}

If you want do find matching objects in both array using their productId,
Here is my solution
First get the productId list from one array then you can easily filter the another array using includes() method
let listSelectedProducts = [{name: "prod2", productId: 1}, {name: "prod2", productId: 2}, {name: "prod2", productId: 3}]
let oldProductsArray = [{name: "prod1", productId: 1}, {name: "prod1", productId: 2}]
let oldIds = oldProductsArray.map(d=>d.productId)
let arrayProductToDelete = listSelectedProducts.filter(d=>oldIds.includes(d.productId))
console.log(arrayProductToDelete)

Related

Add product with array of options to cart

I have a product. I add it to my cart. If such an item exists in the shopping cart, I will increase its counter. It's simple, checking by id.
I have a product, it has an array of options. (milk, cream, sugar, etc.) How to check the cart, and find an existing copy of the product with such options?
function addCoffeeToCart(coffee: CartItem) {
const coffeeAlreadyExistsInCart = cartItems.findIndex(
(cartItem) => cartItem.id === coffee.id
);
const newCart = produce(cartItems, (draft) => {
if (coffeeAlreadyExistsInCart < 0) {
draft.push(coffee);
} else {
draft[coffeeAlreadyExistsInCart].quantity += coffee.quantity;
}
});
setCartItems(newCart);
}
Product:
export interface Coffee {
id: number;
tags: string[];
name: string;
description: string;
photo: string;
price: number;
options: Options[];
}
Options:
export interface Options{
id: number;
name: string;
price: number;
}
Change coffeeAlreadyExistsInCart to this and try again:
const coffeeAlreadyExistsInCart = cartItems.findIndex(
(cartItem) =>
cartItem.id === coffee.id &&
JSON.stringify(cartItem.options) === JSON.stringify(coffee.options)
);
the simplest way of dealing with the count / if exist - is to use an object literal as a map of the existing items in the cart. Then you can have an array of the items with their salient details, and use the object literal to keep track of the count (which automatically deals with the if-exists issue) and then create a cart and a running total of the toal price etc.
The advantage of this approach is that it separates the count logic from the item details in your store and allows for different id and display... eg you can have an id of "c1234-1" and a displayText of "Coffee" and this will still allow you to have your calculartions. Its also good to separate your items details from the cart / store - so that you can easily update prices or availability etc.
Deleting items from the cart is simple - just decrement the count of the item down to 0 and this will adjust the cart total as you do so.
const items = [
{id: 'coffee', name: 'Coffee', price: 5.5},
{id: 'tea', name: 'Tea', price: 3.5},
{id: 'cocoa', name: 'Cocoa', price: 2.5},
{id: 'milk', name: 'Milk', price: .5},
{id: 'sugar', name: 'Sugar', price: 0},
]
const cartItems = {};
function addItemToCart(id) {
cartItems[id]
? cartItems[id] ++
: cartItems[id] = 1;
setCart()
}
function setCart() {
const cart = {total: 0, items:[]};
Object.keys(cartItems).forEach(id => {
const item = items.find(x => x.id === id);
const itemQty = cartItems[id];
cart.items.push({name: item.name, quantity: itemQty, subTotal: item.price * itemQty });
cart.total+= (item.price * itemQty);
})
console.log(cart)
}
addItemToCart('coffee');
// gives {"total": 5.5, "items": [{"name": "Coffee","quantity": 1, "subTotal": 5.5}]}
addItemToCart('coffee');
// gives { "total": 11, "items": [{"name": "Coffee","quantity": 2,"subTotal": 11}]}
addItemToCart('tea');
// gives { "total": 14.5, "items": [{"name": "Coffee","quantity": 2,"subTotal": 11},{"name": Tea","quantity": 1,"subTotal": 3.5} ] }
addItemToCart('milk');
// gives { "total": 15, "items": [{"name": "Coffee","quantity": 2,"subTotal": 11},{"name": "Tea","quantity": 1,"subTotal": 3.5},{"name": "Milk","quantity": 1,"subTotal": 0.5} ]}}
addItemToCart('sugar');
// gives { "total": 15, "items": [{"name": "Coffee","quantity": 2,"subTotal": 11},{"name": "Tea","quantity": 1,"subTotal": 3.5},{"name": "Milk","quantity": 1,"subTotal": 0.5},{"name": "Sugar","quantity": 1,"subTotal": 0} ] }
On the condition of the findIndex you have to add that verification. As answered you can do it with Json.stringify of both arrays but that assumes they are on the same order and complety equal. I prefer to use Array.every and Array.some to ignore those constraints at cost of a little of performance.
const coffeeAlreadyExistsInCart = cartItems.findIndex(
(cartItem) =>
cartItem.id === coffee.id &&
cartItem.options.length === coffee.options.length &&
coffee.options.every(item => cartItem.options.some(elem => elem.id === item.id))
);
Basically with this we are "asking" to each CartItem if they have the same number of options as coffee ( dont know if as to be like that, if you want to just check if have the all the options but not if they have the same number of options you can ignore that condition ) and if every option on the cartItem exists on teh coffee options

Functional Programming exercise I can't figure out

I've been looking at a problem for hours and failing to find a solution. I'm given an array of customer objects.
In each customer object is an array of friends.
In the array of friends is an object for each friend, containing some data, including a name key/value pair.
What I'm trying to solve for: I'm given this customers array and a customer's name. I need to create a function to find if this customer name is in any other customer's friend lists, and if so, return an array of those customer's names.
Below is a customer list. And as an example, one of the customers is Olga Newton. What the code should be doing is seeing that Olga Newton is a customer and is also in the friends lists of Regina and Jay, and should be returning an array of Regina and Jay.
I thought I could do this simply with a filter function, but because the friends list is an array with more objects, this is adding level of complexity for me I can't figure out.
Below is a customer array. The out put should be
['Regina', 'Jay']
and what I've gotten has either been
[{fullCustomerObj1}, {fullCustomerObj2}]
or
[ ]
What am I missing?
Here is the customer array:
var customers = [{
name: "Olga Newton",
age: 43,
balance: "$3,400",
friends: [{
id: 0,
name: "Justice Lara"
}, {
id: 1,
name: "Duke Patrick"
}, {
id: 2,
name: "Herring Hull"
}, {
id: 3,
name: "Johnnie Berg"
}]
}, {
name: "Regina",
age: 53,
balance: "$4,000",
friends: [{
id: 0,
name: "Cheryl Kent"
}, {
id: 1,
name: "Cynthia Wells"
}, {
id: 2,
name: "Gutierrez Waters"
}, {
id: 3,
name: "Olga Newton"
}]
}, {
name: "Jay",
age: 28,
balance: "$3,000",
friends: [{
id: 0,
name: "Cross Barnett"
}, {
id: 1,
name: "Raquel Haney"
}, {
id: 2,
name: "Olga Newton"
}, {
id: 3,
name: "Shelly Walton"
}]
}];
Use filter and map, please.
function friends(c, name){
return c.filter((a) => {
return a.friends.map(b => b.name).includes(name)
}).map(a => a.name);
}
console.log(friends(customers, "Olga Newton"));
// ['Regina', 'Jay']
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
We look to an array (friends[]) inside anther (customers[]), So used two for loops, the first determine witch customer will look for his friends, and the second the array will search inside, then set if statement if the cust name is inside friends[]: adding the customer name to customerFriends[] array, At the end return the customerFriends[].
let cust = "Olga Newton"; // Get the customer name who you look for his friends.
const findFriend = (cust, arrs) => { // Create findFriend function.
let customerFriends = []; // Create an array to set the result to it.
for (let i = 0; i < arrs.length; i++) { // For each Customer.
for (const arr of arrs[i].friends) { // For each Friend.
if (arr.name === cust) { // Use Strict equality to find Customer name in friends[].
customerFriends.push(arrs[i].name); // Add the customer name to the customerFriends[].
}
}
}
return customerFriends;// Return the final results.
}
console.log(findFriend(cust, customers)); // Call the function.

Match multiple object fields with an input value in JS

I am working on a large product datasets and I need to filter through the products list based on the user's input value. My product dataset looks something like this:
const products = [
{id: 0, name: "Product1", brand: "Theraflu", itemCode: "THE110", price: 5.45},
{id: 1, name: "Product2", brand: "Benadryl", itemCode: "BEN121", price: 7.05},
{id: 2, name: "Product3", brand: "Listerine", itemCode: "LIS204", price: 4.55},
{id: 3, name: "Product4", brand: "Tylenol", itemCode: "TYL116", price: 6.10},
];
I was able to filter the product list based on the different fields available in each individual product object like this:
const keys = ["name", "brand", "itemCode"];
const getFilteredProducts = (filterText) => {
const newProducts = products.filter(product => keys.some(key => product[key].toLowerCase().includes(filterText.toLowerCase())));
return newProducts;
}
console.log(getFilteredProducts("Tylenol"));
This code actually works when I filter the product based on individual field. However, when I try to combine different fields like:
console.log(getFilteredProducts("product4 Tylenol"));
The returned value of this is an empty array. Is there a way to achieve this without altering the existing filtering functionality?
Seems like you'll need to search each word of the filterText on its own. Maybe something like this:
const keys = ["name", "brand", "itemCode"];
const getFilteredProducts = (filterText) => {
const filterWords = filterText.split(" ");
const newProducts = [];
for (const word of filterWords) {
newProducts.push(
products.filter(product => keys.some(key =>
product[key].toLowerCase().includes(word.toLowerCase())
))
);
}
return [].concat(...newProducts).filter((value, index, self) =>
self.findIndex((m) => m.id === value.id) === index
);
}
console.log(getFilteredProducts("Tylenol Product3"));

Javascript array difference

I have two arrays like so
data = [{id: 1, name: apple},
{id: 2, name: mango},
{id: 3, name: grapes},
{id: 4, name: banana}]
data2 =[{id: 1, name: apple},
{id: 3, name grapes}]
My Expected result would be:
[{ id: 2, name: mango},
{id:4, name: banana}]
My code is
let finalData =[];
data.forEach(result => {
data2.find(datum => {
if(datum['id'] === result['id]{
finalData.push(result);
}
})
})
I am getting wrong result. What is the simplest code or library that I can use?
Your sample data doesn't make sense, but assuming you mean that all data items that have matching IDs also have matching names and also assuming you want a set of all items where the IDs are the same in the two sets of data, you could use a Set to keep track of which IDs are present in one array then filter the second array by those that have their IDs in the set:
const idsInFirst = new Set(data.map(d => d.id));
const intersection = data2.filter(d => idsInFirst.has(d.id));
The reason why an intermediate Set structure is used is because it allows O(1) lookups after a one-time scan, which is more efficient than repeatedly scanning the first array over and over.
If you meant to say you wanted a difference between data sets (items excluded from data that are in data2), you'd want to negate/inverse things a bit:
const idsToExclude = new Set(data2.map(d => d.id));
const difference = data.filter(d => !idsToExclude.has(d.id));
Edit
After your clarifying edit, it's that second block of code that you'll want.
I would say a good way to do that is filtering your longest array using a function that will validate if the object id is present in both arrays. Check this example:
const data = [
{id: 1, name: 'apple'},
{id: 2, name: 'mango'},
{id: 3, name: 'grapes'},
{id: 4, name: 'banana'}
]
const data2 =[
{id: 1, name: 'apple' },
{id: 3, name: 'grapes' }
]
const longest = data.length > data2.length ? data : data2;
const shortest = data.length <= data2.length ? data : data2;
const finalData = longest.filter( obj => !shortest.find( o => o.id === obj.id ) )
console.log(finalData)
Good luck!

How to reformat a JSON array into another format "grouping" based on different keys

Question: How can I reformat this JSON array by "grouping" via different keys, using ReactJS?
I have a JSON array as :
[
{Product: "Shoes", Sold: 5, Bought : 0, Reversed : 2} ,
{Product: "Table", Sold: 2, Bought : 0, Reserved : 4}
]
The reason for this is the data type I'm working with, and on realizing I need to visualize this data in a different way (due to one of the graph packages I am using) I need to structure this data as:
[
{
Status: "Sold",
Shoes : 5,
Table : 2
} ,
{
Status: "Bought",
Shoes : 0,
Table : 0
} ,
{
Status: "Reserved",
Shoes : 2,
Table : 4
}
]
So I'm grouping the data into the keys other than Product, and then the keys after this are Product with the Value being the Product and it's "status".
Frankly, I am at a complete loss as to what to do, as I'm thinking the code required to generate this would be quite convoluted, so I'm very open to know if this just is too much work.
const data = [
{
Product: "Shoes",
Sold: 5,
Bought : 0,
Reserved : 2
} , {
Product: "Table",
Sold: 2,
Bought : 0,
Reserved : 4
}
];
let resultData = [];
Object.keys(data[0]).forEach((key, idx) => {
if (idx !== 0) {
let resultUnit = {
Status: key,
};
data.forEach(item => {
return resultUnit = {
...resultUnit,
[item.Product]: item[key],
}
})
resultData.push(resultUnit);
}
})
console.log(resultData);
// 0: {Status: "Sold", Shoes: 5, Table: 2}
// 1: {Status: "Bought", Shoes: 0, Table: 0}
// 2: {Status: "Reserved", Shoes: 2, Table: 4}
You can do this using the Array.reduce function. (Actually, two reduce functions).
Here's an extensible solution that allows for other statuses.
Note that I changed everything to lowercase, as is standard convention.
const items = [
{product: "Shoes", sold: 5, bought : 0, reserved : 2} ,
{product: "Table", sold: 2, bought : 0, reserved : 4}
]
//We declare the status types here.
const keys = ["sold", "bought", "reserved"];
// Just create the initial 'statuses' array.
function initAcc(keys) {
return keys.map((key) => {
return {
status: key
}
});
}
//Here we are iterating over each item, getting it to return a single accumulator array each time.
const newItems = items.reduce((acc, cur) => {
return addItemToAccumulator(acc, cur);
}, initAcc(keys));
console.log(newItems);
// This function maps of the accumulator array (ie. over each status).
function addItemToAccumulator(acc, item) {
return acc.reduce((acc, statusLine) => {
//Find the count from the existing status if it exists,
//Add the current items count for that status to it.
const itemCount = item[statusLine.status] + (statusLine[item.product] || 0);
//Return a modified status, with the new count for that product
return [
...acc,
{
...statusLine,
[item.product]: itemCount
}
];
}, []);
}
Lets just do a simple loop function and create a couple objects to clearly solve the problem here:
const data = [YOUR_INITIAL_ARRAY];
let Sold, Bought, Reserved = {};
data.forEach(({Product, Sold, Bought, Reserved})=> {
Sold[Product] = Sold;
Bought[Product] = Bought;
Reservered[Product] = Reserved;
});
let newArray = [Sold, Bought, Reserved];
I think you can see where this is going ^ I see a few others have given complete answers, but try and go for the clear understandable route so it makes sense.
All you have to do after this is set the status which i'd do off an enum and you are good

Categories