Related
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)
I am building a chart for monthly data which would have the x axis as wk1 - wk4 and y axis being the amount of goods etc. I was able to build out a solution but the problem lies when there is no data for a particular week. This is my code below.
const byAmount = obj => {
const res = [];
const keys = Object.keys(obj);
keys.forEach(key => {
res.push({
week: `wk${key}`,
amount: obj[key]
});
});
return res.sort((a, b) => a.amount - b.amount).slice(0, 5);;
};
const getWeeklyFromMonth = (arr, month) => {
const week = arr.map(a => ({ ...a, week: Math.floor((moment(a.dateScanned.$date).date() - 1) / 7) + 1 }));
let dataForMonth = [];
let total;
week.map(data => {
if (moment(data.dateScanned.$date).format("MMM") === month) {
dataForMonth.push(data);
const totalPerWeek = dataForMonth.reduce((acc, cur) => {
acc[cur.week] = acc[cur.week] + cur.amount || cur.amount;
return acc;
}, {});
total = totalPerWeek;
}
});
return byAmount(total);
}
When I run this I get the below:
[
{ week: 'wk1', amount: 14 },
{ week: 'wk2', amount: 27 },
{ week: 'wk4', amount: 43 }
]
This is fine but I want to populate the array with 0 if there is no data say for week 3. I would want it to be this
[
{ week: 'wk1', amount: 14 },
{ week: 'wk2', amount: 27 },
{ week: 'wk3', amount: 0 },
{ week: 'wk4', amount: 43 }
]
I was thinking of having an array of like [1, 2, 3, 4] and if the array includes the week number, pop it out of the array and then the remaining item should be used to populate it but I find myself scratching my head. Does anyone know a decent way to do this?
Thank you in advance.
You can try this:
const byAmount = obj => {
const res = [];
const keys = Object.keys(obj);
const [min, max] = [Math.min(...keys), Math.max(...keys)];
for(let key = min; key <= max; key++) {
res.push({
week: `wk${key}`,
amount: obj[key] || 0
});
}
return res.sort((a, b) => a.amount - b.amount).slice(0, 5);;
};
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
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);
My state looks like so:
items: [
{ id: 1, name: 'banana', price: 100, quantity: 1 },
{ id: 2, name: 'apple', price: 200, quantity: 1 },
{ id: 3, name: 'blueberry', price: 300, quantity: 1 }
]
cart: []
I have a function where I push the item to the cart:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [...cart, { ...item, quantity: itemQuantity }],
total: total + (price * itemQuantity)
})
}
}
I'm checking to see if the item exists before adding it to avoid duplicates. What I want to happen is if the item is already added to the cart, I want to find that object and change its quantity value.
Is this possible to do?
I think the solution below would work for you without having to update your design much. You really just have to call Array.prototype method reduce on an array of items you wish to add to the cart. Make sure to pass your cart's current state as the initial value to reduce (pass it as the second argument). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
With some simple logic, you could just check to see if the item id already exists in the cart (which is just an object literal keyed by item id). If it doesn't, add the object and set the quantity property to 1 - if it does, just increment the quantity property by 1.
Hopefully the example below is clear and can help you out:
//an array of items we want to add to the cart
var items = [{
id: 1,
name: 'banana',
price: 100,
quantity: 1
},
{
id: 2,
name: 'apple',
price: 200,
quantity: 1
},
{
id: 3,
name: 'blueberry',
price: 300,
quantity: 1
}
];
//function utilizing reduce to update our cart object
function updateCart(cartToUpdate, itemsToAdd) {
itemsToAdd.reduce(function(cart, cartItem) {
if (!cart[cartItem.id]) {
cart[cartItem.id] = {
name: cartItem.name,
price: cartItem.price,
quantity: 1
}
} else {
cart[cartItem.id].quantity++;
}
return cart;
}, cart);
}
//implement cart as an object keyed on item id
var cart = {
1: {
name: 'banana',
price: 100,
quantity: 1
}
};
//i manually added a single banana to the cart so we can show that our update function works properly
console.log('Cart before update:');
console.log(cart);
updateCart(cart, items);
console.log('Cart after update:');
console.log(cart);
//from here it's really easy to see how many of each item we have in the cart:
Object.keys(cart).forEach(function(key) {
console.log(`The customer has ${cart[key].quantity} ${cart[key].name}(s) in their cart`)
});
//
You could do something like this:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const index = cart.findIndex(x => x.id === id);
let quantity = itemQuantity;
const newCart = index === -1
? [...cart, {...item, quantity }]
: cart.map((it, i) => {
if (i === index) {
quantity = it.quantity + quantity;
return { ...it, quantity }
} else return it
});
this.setState({
cart: newCart,
total: total + (price * quantity)
});
}
Yes, just leverage the i const that you've already defined!
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
const newCart = [...cart]
const newTotal = total + (price * itemQuantity)
if (i !== -1) {
const newItem = { ...item, quantity: itemQuantity + 1 }
newCart[i] = newItem
this.setState({
cart: newCart,
total: newTotal,
})
return
}
newCart.push({ ...item, quantity: itemQuantity })
this.setState({
cart: newCart,
total: newTotal,
})
}
Note, it was unclear how you wanted the total functionality to work, so I've left that as it is. This, however will update the item you're looking for.
I had to check to see whether the item was added to the cart before pushing a new object with that item.
I then had to find the index of that item in the cart and update its quantity.
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.findIndex(x => x.id === id)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [
...cart,
{ ...item, quantity: itemQuantity }
]
})
} else {
this.setState({
cart: [
...cart.slice(0, i),
{ ...cart[i], quantity: cart[i].quantity + itemQuantity },
...cart.slice(i + 1)
]
})
}
this.setState({
total: total + (price * itemQuantity)
})
}