Combining objects if condition matches - javascript

I have an array boxSize that has a total count of boxes for small and large boxes. I would like to combine these 2 separate objects into one if they have the same date and warehouse name
For example here is the boxSize array:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
I attempted to loop through the array like this in the code snippet below:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
var obj = {};
for(let i = 0; i < boxSize.length; i++){
var date = boxSize[i].deliveryWeek;
// Get previous date saved inside the result
var p_date = obj[date] || {};
// Merge the previous date with the next date
obj[date] = Object.assign(p_date, boxSize[i]);
}
// Convert to an array
var result = Object.values(obj);
console.log(result);
I am having trouble coming up with the logic and finding up examples to meet these requirements condition.
How can I end up with an array that looks something similar to this when the objects have the same warehouse and deliveryWeek:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSizeSmall: 'small',
smallTotal: 5,
boxSizeLarge: 'large',
largeTotal: 9
}
]
console.log(boxSize)

You could get a result like that by aggregating them with a map and then doing some transformations on the values like this:
const boxSize = [{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'small',total:5},{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'large',total:9}];
const keyFor = (item) => `${item.warehouse}:${item.deliveryWeek}`;
const map = new Map();
boxSize.forEach((box) => {
const key = keyFor(box);
if (!map.has(key)) map.set(key, []);
map.get(key).push(box);
});
const result = Array.from(map).map(([, values]) => ({
warehouse: values[0].warehouse,
deliveryWeek: values[0].deliveryWeek,
...values.reduce((obj, item) => ({
...obj,
["boxSize" + item.boxSize[0].toUpperCase() + item.boxSize.slice(1)]: item.boxSize,
[item.boxSize + "Total"]: item.total,
}), {}),
}));
console.log(result);
but this is really complicated and it's hard to use the resulting object in practice. Also, duplicate entries will be overwritten by the latest entry... I recommend using an array in the resulting object to avoid all of these:
const boxSize = [{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'small',total:5},{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'large',total:9}];
const keyFor = (item) => `${item.warehouse}:${item.deliveryWeek}`;
const map = new Map();
boxSize.forEach((box) => {
const key = keyFor(box);
if (!map.has(key)) map.set(key, []);
map.get(key).push(box);
});
const result = Array.from(map).map(([, values]) => ({
warehouse: values[0].warehouse,
deliveryWeek: values[0].deliveryWeek,
details: values.map(({ boxSize, total }) => ({ boxSize, total })),
}));
console.log(result);
which is arguably easier to implement and use compared to the former.

let boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
let combinedBoxes = [];
for(let i = 0; i < boxSize.length; i++){
let currentBox = boxSize[i];
let boxes = boxSize.filter(box => box.warehouse == currentBox.warehouse && box.deliveryWeek == currentBox.deliveryWeek)
let small = boxes.filter(box => box.boxSize == "small")[0]
let large = boxes.filter(box => box.boxSize == "large")[0]
combinedBoxes.push({
warehouse: currentBox.warehouse,
deliveryWeek: currentBox.deliveryWeek,
smallTotal: small.total,
largeTotal: large.total
})
boxSize = boxSize.filter(box => box.warehouse != currentBox.warehouse && box.deliveryWeek != currentBox.deliveryWeek)
}
console.log(combinedBoxes)

Related

Divide object array elements into groups of n each javascript

I have an Object as below:
const boxOfFruits = {
apples: [
{
name: "Kashmiri",
},
{
name: "Washington",
},
{
name: "Himalayan",
},
{
name: "Fuji",
}
],
oranges: [
{
name: "Nagpur",
},
{
name: "Clementine",
},
],
mangoes: [
{
name: "Totapuri",
},
{
name: "Alphonso",
},
{
name: "Langda",
},
],
}
I want to divide these fruits into boxes; maximum of n each, let's say where n is 3 and apples, oranges and mangoes are equally distributed.
So the output in this case would be:
box_1 = [{name: "Kashmiri"}, {name: "Nagpur"},{name: "Totapuri"}];
box_2 = [{name: "Washington"}, {name: "Clementine"},{name: "Alphonso"}];
box_3 = [{name: "Himalayan"},{name: "Langda"}, {name: "Fuji"}];
The type of fruits(apple,oranges,etc)/keys in object can increase/decrease and n is also variable. In case total fruits are less than n, then it would be just 1 box of fruits.
What I have tried so far:
Using Lodash, I am calculating the minimum and the maximum fruits in a single type:
const minFruitType = _.min(Object.values(basket).map((eachBasket: any) => eachBasket.length));
Total teams will the sum of the fruits / n
Will distribute the minimum fruits (l) in the first l boxes and fill the rest with the remaining fruits at every iteration while at the start of every iteration will calculate the minimum type of fruits again.
You can use Object.values(), array#reduce and array#forEach to transform your object.
const boxOfFruits = { apples: [ { name: "Kashmiri", }, { name: "Washington", }, { name: "Himalayan", }, ], oranges: [ { name: "Nagpur", }, { name: "Clementine", }, ], mangoes: [ { name: "Totapuri", }, { name: "Alphonso", }, { name: "Langda", }, ], },
result = Object.values(boxOfFruits).reduce((r, arr) => {
arr.forEach((o,i) => {
const key = `box_${i+1}`;
r[key] ??= r[key] || [];
r[key].push(o)
});
return r;
},{});
console.log(result);
The easiest way would be to use lodash.js's zip() function:
const boxes = _.zip( Object.values(boxOfFruits) );
Note that _.zip() will give you undefined values when the source arrays are different lengths, so you'll need/want to filter those out:
const boxes == _.zip( Object.values(boxOfFruits) )
.map(
box => box.filter(
x => x !== undefined
)
);
But that will not distribute the fruits evenly. For that, it shouldn't get much for difficult than this:
function distribute(boxOfFruits, n) {
const boxes = [];
const fruits = Object.keys(boxOfFruits);
for ( const fruit of fruits ) {
let i = 0;
const items = boxOfFruits[fruit];
for (const item of items) {
boxes[i] = !boxes[i] ?? [];
boxes[i] = boxes[i].push(item);
++i;
i = i < n ? i : 0 ;
}
}
return boxes;
}
A modified version of #Nicholas Carey's answer worked for me:
function distribute(boxOfFruits, n) {
let boxes = [];
let totalFruits = Object.values(boxOfFruits)
.reduce((content, current) => content + current.length, 0);
let maxBoxes = Math.ceil(totalFruits / 4);
Object.values(boxOfFruits).forEach((fruits) => {
let i = 0;
fruits.forEach((fruit) => {
boxes[i] ??= boxes[i] || [];
boxes[i].push(fruit);
++i;
i = i < (n+1) ? i : 0;
});
});
// Extra boxes created, redistribute them to
// starting boxes
let newBoxes = teams.slice(0, maxBoxes);
let pendingBoxes = teams.slice(maxBoxes);
let pendingFruits = pendingBoxes.flat();
let distributedBoxes = newBoxes.map((eachBox) => {
let required = n - eachBox.length;
if (required > 0) {
eachBox.push(...pendingFruits.splice(0, required));
}
return eachBox;
});
return distributedBoxes;
}
Code is pretty much the same as Nicholas's accept the below changes:
Directly fetched the values and iterated over those
empty array creation was failing, this way works
and checking on the max box size with n+1 instead of n

How to add objects to an array and avoid duplicate keys for an object?

I have these 2 arrays:
productsForSale = ['eggs', 'eggs', 'bread', 'milk'];
soldPrice = [2.70, 2.50, 1.97, 3.29];
yes, the values of the first 2 elements ("eggs") are different but that's meant to be for this question. Now I want to create an array of objects that will look like this:
[
{
eggs: 2.70
},
{
eggs: 2.50
},
{
bread: 1.97
},
{
milk: 3.29
}
]
So far I have this code:
var obj = {};
var arr = [];
productsForSale.forEach((key, i) => {
obj[key] = soldPrice[i];
arr.push(obj);
});
But I don't get the expected output. Can anyone point me in the right dirrection? Thanks
You can use map().
const productsForSale = ["eggs", "eggs", "bread", "milk"];
const soldPrice = [2.7, 2.5, 1.97, 3.29];
const output = productsForSale.map((el, i) => ({ [el]: soldPrice[i] }));
console.log(output);

React.js: How to find duplicates for properties in an array of object and put a progressive number on that field

I have an array of object and each object is for example :
const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}]
for the first 2 element for the property "name" I have the name "john" that is duplicate.
How can I modify the rendered names like that:
const myArr=[{name:"john (1 of 2)",id:1}{name:"john (2 of 2)",id:2}{name:"mary",id:3}]
Thanks in advance!
Reduce the input array into a map by name (i.e. group by name property), and map the array of values to the result array. If the group array has more than 1 element in it then sub-map the group to include the numbering. Flatten the overall result.
const myArr = [
{ name: "john", id: 1 },
{ name: "john", id: 2 },
{ name: "mary", id: 3 }
];
const res = Object.values(
myArr.reduce((groups, current) => {
if (!groups[current.name]) {
groups[current.name] = [];
}
groups[current.name].push(current);
return groups;
}, {})
).flatMap((value) => {
if (value.length > 1) {
return value.map((current, i, arr) => ({
...current,
name: `${current.name} (${i + 1} of ${arr.length})`
}));
}
return value;
});
console.log(res);
You can do use reduce(), filter(), and flat() and do this:
const myArr = [
{name:"john", id:1},
{name:"john", id:2},
{name:"mary", id:3}
]
const res = Object.values(myArr.reduce((acc, curr) => {
const total = myArr.filter(({ name }) => name === curr.name).length;
if(!acc[curr.name]) {
acc[curr.name] = [
{...curr}
]
} else {
const currentSize = acc[curr.name].length;
if(currentSize === 1) {
acc[curr.name][0].name = `${acc[curr.name][0].name} (1 of ${total})`
}
acc[curr.name].push({
...curr,
name: `${curr.name} (${currentSize + 1} of ${total})`
})
}
return acc;
}, {})).flat();
console.log(res);
const myArr = [{name:"john",id:1}, {name:"john",id:2}, {name:"mary",id:3}];
const namesArray = myArr.map(elem => elem.name);
const namesTraversed = [];
let currentCountOfName = 1;
let len = 0;
myArr.forEach(elem => {
len = namesArray.filter(name => name === elem.name).length;
if (len > 1) {
if (namesTraversed.includes(elem.name)) {
namesTraversed.push(elem.name);
currentCountOfName = namesTraversed.filter(name => name === elem.name).length;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
} else {
namesTraversed.push(elem.name);
currentCountOfName = 1;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
}
}
});
console.log(myArr);
Check if this helps you
const myArr = [{
name: "john",
id: 1
}, {
name: "john",
id: 2
}, {
name: "mary",
id: 3
}]
// to keep a track of current copy index
let nameHash = {}
const newMyArr = myArr.map(ele => {
const noOccurence = myArr.filter(obj => obj.name ===ele.name).length;
if(noOccurence > 1){
// if there are multiple occurences get the current index. If undefined take 1 as first copy index.
let currentIndex = nameHash[ele.name] || 1;
const newObj = {
name: `${ele.name} (${currentIndex} of ${noOccurence})`,
id: ele.id
}
nameHash[ele.name] = currentIndex+ 1;
return newObj;
}
return ele;
})
console.log(newMyArr);

How can I solve the problem of NaN value?

I want to sum the values of an array but I get an error with NaN
could you help me please?
const products = [
{
id: 1,
title: 'T-Shirt',
price: 98,
quantity: 10
},
{
id: 2,
title: 'Shoes',
price: 70,
quantity: 17
}
]
let cartItems = []
let parsIntPrice
let totalPrice = 0
const shoppingCart = (myId) => {
localStorage.setItem('myProduct', JSON.stringify(products))
const getStorage = localStorage.getItem('myProduct')
const finalProducts = JSON.parse(getStorage)
// SHOW EACH ITEM SELECTED
let selectedResult = finalProducts.filter(item => item.id === myId)
for (let product in selectedResult) {
console.log(selectedResult[product].title)
// CALCULATE TOTAL PRICES
parsIntPrice = selectedResult[product].price
totalPrice = cartItems.reduce((firstItem, secondItem) => firstItem + secondItem.parsIntPrice, 0)
console.log(`Total Price is: ${totalPrice}`)
}
}
Your cartItems array is empty, so reduce doesn't iterate it

Array compare with for each performance issue

I have two arrays with array of objects as follows and one array will have more than 10k records and other have below 100 records
let bigArray = [{id:1, name:"Raj", level:0}, {id:2, name:"sushama", level:2}, {id:3, name:"Sushant", level:0}, {id:4, name:"Bhaskar", level:2},....upto 30k records]
let smallArray = [{id:2, name:"sushama"}, {id:3, name:"Sushant"}....upto 100 records]
I want to find where in the index of bigArray in which the object from smallArray resides and add to another array say indexArray I tried below
let indexArray = [];
bigArray.forEach((element, i) => {
smallArray.forEach(ele => {
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i); return;
}
});
});
But it takes time. What would be the fastest approach?
You can turn your O(N^2) approach into an O(N) approach by reducing the bigArray into an object indexed by a key made up from the name and id. Join the name and id by a character that isn't contained in either, such as _:
const indexArray = [];
const bigArrayIndiciesByNameAndId = bigArray.reduce((a, { name, id }, i) => {
a[name + '_' + id] = i;
return a;
}, {});
smallArray.forEach(ele => {
const keyToFind = ele.name + '_' + ele.id;
const foundIndex = bigArrayIndiciesByNameAndId[keyToFind];
if (foundIndex) {
indexArray.push(foundIndex);
}
});
You could take a Map and map the found indices.
const getKey = ({ id, name }) => [id, name].join('|');
let bigArray = [{ id: 1, name: "Raj", level: 0 }, { id: 2, name: "sushama", level: 2 }, { id: 3, name: "Sushant", level: 0 }, { id: 4, name: "Bhaskar", level: 2 }],
smallArray = [{ id: 2, name: "sushama" }, { id: 3, name: "Sushant" }],
map = new Map(bigArray.map((o, i) => [getKey(o), i]))
indexArray = smallArray.map((o) => map.get(getKey(o)));
console.log(indexArray);
return will not "break" the forEach loop. A forEach can't be stopped. The forEach callback function will be called one time per items always. When you find the element, continue running the forEach loop is a waste of resouces.
You should use for instead:
let indexArray = [];
bigArray.forEach((element, i) => {
for (var ii = 0; ii < smallArray.length; ii++) {
var ele = smallArray[ii];
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i);
break; // This will break the "for" loop as we found the item
}
}
});
TIP: Always have a good indentation in your code. Actually your code is really bad indented to identify code blocks at first sight. I fixed it in this example.

Categories

Resources