Finding proprieties of accumulator in JS - javascript

I am running this accumulator, it works great but I find it challenging to add some proprities in the same object :
let expenseCategories = expenseObj.reduce((acc, obj) => {
let category = obj['category'];
// let amount = obj['amount'];
if (category in acc) {
acc[category] += 1;
} else {
acc[category] = 1;
}
return acc;
}, {});
{Transportation: 2, Food: 1, Clothes: 1, Bills: 2, Fun: 1, …}
My initial object also contains a transaction amount
{amount: 10, category: "Transportation", date: 20190510, expense: true, ...
{amount: 20, category: "Drinks", date: 20190510, expense: true, ...
{amount: 30, category: "Bills", date: 20190510, expense: true, ...
{amount: 40, category: "Bills", date: 20190510, expense: true, ...
My goal here is to calculate sum of each categories like bills in the example above would be 70.
I am looking to add this info to display some chart, the expected array looks something like that :
0: {name: "Transportation", value: 2, total: 123}
1: {name: "Food", value: 1, total: 456}
2: {name: "Clothes", value: 1, total: 789}
This is my attempt with the correct data I already have:
let outExpense = Object.entries(expenseCategories).map(([name, value]) => ({ name, value }));
This should be dynamic because I don't know categories prior to running code. These are user inputs. Please advise. Thanks

You are counting the occurrences of each category. You need to change your implementation a little bit. Instead of creating a number as value, you can create the object you need in the output array as value. Then, simply use Object.values() on the accumulator object to get the array of grouped values
const expenseObj = [{amount: 10, category: "Transportation", date: 20190510, expense: true},
{amount: 20, category: "Drinks", date: 20190510, expense: true},
{amount: 30, category: "Bills", date: 20190510, expense: true},
{amount: 40, category: "Bills", date: 20190510, expense: true}]
let groupCategories = expenseObj.reduce((acc, obj) => {
let { category, amount } = obj;
if (category in acc) {
acc[category].value++;
acc[category].total += amount;
} else {
acc[category] = { name: category, value: 1, total: amount };
}
return acc;
}, {});
console.log(Object.values(groupCategories))
This is how the accumulator would look like after the changes. If the current object's category already exists in the accumulator, increment the value and total. Else, add the category as key and a new object as it's value
{
"Transportation": {
"name": "Transportation",
"value": 1,
"total": 10
},
"Drinks": {
"name": "Drinks",
"value": 1,
"total": 20
},
"Bills": {
"name": "Bills",
"value": 2,
"total": 70
}
}

Related

Javascript Algorithm for inventory

I have a problem, which I solved already but I feel like my implementation is very basic and could definitely learn a better way to do this.
Given 2 array of objects, one for the sales that my customers are demanding, and one for the purchases that I'm doing to my provider, I need to be able to place the orders and know when I will be able to satisfy them. I will only work with one product, to make it simpler.
I'm very new at coding this case of problems, so I would really apreciate a point in the right direction. Maybe there's a data structure that I haven't used that could help here.
Properties:
'created': when the sales order was created
'quantity': how many items the customer wants
const sales= [{
'id': 'S1',
'created': '2020-01-02',
'quantity': 6
}, {
'id': 'S2',
'created': '2020-11-05',
'quantity': 2
}, {
'id': 'S3',
'created': '2019-12-04',
'quantity': 3
}, {
'id': 'S4',
'created': '2020-01-20',
'quantity': 2
}, {
'id': 'S5',
'created': '2019-12-15',
'quantity': 9
}];
Properties:
'receiving': when we expect to receive the product
'quantity': how many we will be receiving
const purchases= [{
'id': 'P1',
'receiving': '2020-01-04',
'quantity': 4
}, {
'id': 'P2',
'receiving': '2020-01-05',
'quantity': 3
}, {
'id': 'P3',
'receiving': '2020-02-01',
'quantity': 5
}, {
'id': 'P4',
'receiving': '2020-03-05',
'quantity': 1
}, {
'id': 'P5',
'receiving': '2020-02-20',
'quantity': 7
}];
My code so far. I'm returnign an array that for reach sales, it shows when I will be able to satisfy it. The problem that I'm running with the current implementation is that I cannot cover all the cases.
function allocate(salesOrders, purchaseOrders) {
//ordering sales and purchases by date
const orderedSales = salesOrders.sort((a, b) => a.created.localeCompare(b.created));
const orderedPurchases = purchaseOrders.sort((a, b) => a.receiving.localeCompare(b.receiving));
console.log(orderedSales)
console.log(orderedPurchases)
let stock = 0;
const result = [];
purchaseIndex = 0;
orderedSales.forEach((sale, index) => {
const order = orderedPurchases[purchaseIndex];
if (order) {
console.log("Processing order", sale.id)
console.log(`Leftover stock = ${stock}`)
stock += order.quantity
console.log(`new stock = ${stock}`)
stock = stock - sale.quantity;
console.log(`Sustracting = ${sale.quantity}`)
console.log(`Remaining = ${stock}`)
while (stock < 0) {
purchaseIndex++
console.log(`Demand NOT satified, moving to next purchase order with index ${purchaseIndex}`)
stock += order.quantity
console.log(`Current stock = ${stock}`)
increaseOrder = false;
}
//demand has been satisfied
console.log(`Demand for ${sale.id} was satified with purchase ${order.id}, time is ${order.receiving}, moving to next purchase order`)
result.push({
id: sale.id,
availabilityDate: order.receiving
})
purchaseIndex++
console.log("Next sale ++++++++")
console.log(" ++++++++")
}
});
console.log(result);
}
allocate(salesOrders, purchaseOrders)
I think your approach is mostly ok. I would just have some remarks/questions:
is it ok to always have stock start at 0? They are never left over? According to me, it should be one of the parameters of the allocate function
you should handle the case where the purchases are not enough to satisfy the sales (which is the case in your example data set) => with your current code your while loop could go over the max allowed index of the purchases array and throw an exception
if a sale can be satisfied before its creation date, should the availability date be before the creation date or should it be forced to the creation date? (if sales are always in the past and purchases in the future, this question doesn't make sense)
Here is how I would tackle the problem:
console.log(allocate(salesOrders, purchaseOrders));
function allocate(_sales, _purchases) {
const sales = structureData(_sales, "created");
const purchases = structureData(_purchases, "receiving");
const LAST_PURCHASE_INDEX = purchases.length - 1;
let stock = 0; // in real life, maybe this should be an input as well since it might not always start from 0?
let availabilityDate = sales[0].date; // timestamp of stock availability, initialized to first sale timestamp
let availabilityDateString = sales[0].created; // date in string format of stock availability, initialized to first sale created date
let purchaseIndex = 0; // index of the next purchase to process
const result = [];
// loop on sales
for (let sale of sales) {
const requiredQuantity = sale.quantity;
const saleId = sale.id;
// As long as we don't have enough stock, process the next purchase if there is any
while (stock < requiredQuantity && purchaseIndex <= LAST_PURCHASE_INDEX) {
const purchase = purchases[purchaseIndex];
stock += purchase.quantity;
availabilityDate = purchase.date;
availabilityDateString = purchase.receiving;
purchaseIndex++;
}
if (stock >= requiredQuantity) { // we have enough stock and push the availability date
result.push({
id: saleId,
availabilityDate:
availabilityDate > sale.date ? availabilityDateString : sale.created, // It could be simplified to availabilityDate if it's ok to have an availabilityDate before the sales creation
});
stock -= sale.quantity;
} else { // we don't have enough stock and there are no purchases left, so we need more purchases
result.push({ id: saleId , availabilityDateString: "Not anytime soon, need more purchases"});
}
}
return result;
}
// utils to sort orders and add a date timesteamp for easier date comparison
function structureData(orders, dateField) {
return orders
.map((order) => ({ ...order, date: new Date(order[dateField]).getTime() }))
.sort((o1, o2) => o1.date - o2.date);
}
I would do it like this. First I would create a collection of events that have type, date, id, and quantity fields, then sort them by date. This intermediate format might look like this:
[
{type: "sale", date: "2019-12-04", id: "S3", quantity: 3},
{type: "sale", date: "2019-12-15", id: "S5", quantity: 9},
{type: "sale", date: "2020-01-02", id: "S1", quantity: 6},
{type: "purchase", date: "2020-01-04", id: "P1", quantity: 4},
{type: "purchase", date: "2020-01-05", id: "P2", quantity: 3},
{type: "sale", date: "2020-01-20", id: "S4", quantity: 2},
{type: "purchase", date: "2020-02-01", id: "P3", quantity: 5},
{type: "purchase", date: "2020-02-20", id: "P5", quantity: 7},
{type: "purchase", date: "2020-03-05", id: "P4", quantity: 1},
{type: "sale", date: "2020-11-05", id: "S2", quantity: 2}
]
Then I would fold this list of events into a structure with onHand, completed and open properties by checking each event. If it's a purchase, then we add its quantity to onHand. Then we loop through the existing open events (plus the current one if it's a sale), creating new entries to add to the existing completed array if the quantity is not bigger than onHand, and adding to a new open array if it's too large. The code could look like this:
const process = (initial) => (sales, purchases) => [
...sales.map (({created, ...rest}) => ({type: 'sale', date: created, ...rest})),
...purchases.map (({receiving, ...rest}) => ({type: 'purchase', date: receiving, ...rest})),
] .sort (({date: d1}, {date: d2}) => d1 < d2 ? -1 : d1 > d2 ? 1 : 0) .reduce ((
{onHand, open: o, completed},
{type, date, quantity, id, open = [...o, ... (type == 'sale' ? [{quantity, date, id}] : [])]}
) => open .reduce (
({onHand, open, completed}, {quantity, date: saleDate, id}) => quantity <= onHand
? {onHand: onHand - quantity, open, completed: completed .concat ({date, id, quantity})}
: {onHand, open: open .concat ({quantity, date: saleDate, id}), completed},
{onHand: onHand + (type == 'purchase' ? quantity : 0) , open: [], completed}
), initial)
const allocate = process ({onHand: 0, open: [], completed: []})
const salesOrders = [{id: "S1", created: "2020-01-02", quantity: 6}, {id: "S2", created: "2020-11-05", quantity: 2}, {id: "S3", created: "2019-12-04", quantity: 3}, {id: "S4", created: "2020-01-20", quantity: 2}, {id: "S5", created: "2019-12-15", quantity: 9}]
const purchaseOrders = [{id: "P1", receiving: "2020-01-04", quantity: 4}, {id: "P2", receiving: "2020-01-05", quantity: 3}, {id: "P3", receiving: "2020-02-01", quantity: 5}, {id: "P4", receiving: "2020-03-05", quantity: 1}, {id: "P5", receiving: "2020-02-20", quantity: 7}]
console .log (allocate (salesOrders, purchaseOrders))
.as-console-wrapper {max-height: 100% !important; top: 0}
This structure we folded to serves as our overall output, something like this:
{
onHand: 0,
completed: [
{date: "2020-01-04", id: "S3", quantity: 3},
{date: "2020-01-20", id: "S4", quantity: 2},
{date: "2020-02-01", id: "S1", quantity: 6},
{date: "2020-03-05", id: "S5", quantity: 9}
],
open: [
{date: "2020-11-05", id: "S2", quantity: 2}
]
}
But that suggests an improvement. We could make this function reentrant. We could save the output, then next time we need to add events, we could simply pass that back through the function with new sales and purchases. This would give you an updated list. It seems like a handy feature, and it's not any more difficult to add. The only real API change is that you now pass the current value in every call, with some default values for the initial call. Here is one version:
const process = (initial, sales, purchases) => [
...sales.map (({created, ...rest}) => ({type: 'sale', date: created, ...rest})),
...purchases.map (({receiving, ...rest}) => ({type: 'purchase', date: receiving, ...rest})),
] .sort (({date: d1}, {date: d2}) => d1 < d2 ? -1 : d1 > d2 ? 1 : 0) .reduce ((
{onHand, open: o, completed},
{type, date, quantity, id, open = [...o, ... (type == 'sale' ? [{quantity, date, id}] : [])]}
) => open .reduce (
({onHand, open, completed}, {quantity, date: saleDate, id}) => quantity <= onHand
? {onHand: onHand - quantity, open, completed: completed .concat ({date, id, quantity})}
: {onHand, open: open .concat ({date: saleDate, id, quantity}), completed},
{onHand: onHand + (type == 'purchase' ? quantity : 0) , open: [], completed}
), initial)
const salesOrders = [{id: "S1", created: "2020-01-02", quantity: 6}, {id: "S2", created: "2020-11-05", quantity: 2}, {id: "S3", created: "2019-12-04", quantity: 3}, {id: "S4", created: "2020-01-20", quantity: 2}, {id: "S5", created: "2019-12-15", quantity: 9}]
const purchaseOrders = [{id: "P1", receiving: "2020-01-04", quantity: 4}, {id: "P2", receiving: "2020-01-05", quantity: 3}, {id: "P3", receiving: "2020-02-01", quantity: 5}, {id: "P4", receiving: "2020-03-05", quantity: 1}, {id: "P5", receiving: "2020-02-20", quantity: 7}]
const initialValues = {onHand: 0, open: [], completed: []}
const currentState = process (initialValues, salesOrders, purchaseOrders)
console .log ('initial load: ', currentState)
const additionalSalesOrders = [{id: "S6", created: "2021-03-07", quantity: 3}, {id: "S7", created: "2021-04-21", quantity: 10}, {id: "S3", created: "2021-06-14", quantity: 5}]
const additionalPurchaseOrders = [{id: "P6", receiving: "2021-05-20", quantity: 8}]
const nextState = process (currentState, additionalSalesOrders, additionalPurchaseOrders)
console .log ('after new events: ', nextState)
.as-console-wrapper {max-height: 100% !important; top: 0}

How to build a tree from JSON data ? by modifying the entries like shown below, using any programming language [duplicate]

This question already has answers here:
Build tree array from flat array in javascript
(34 answers)
Closed 1 year ago.
Input:
const data = [
{
category: 'Techonology',
subcategory: 'laptop',
sale: 19000,
profit: 909049,
},
{
category: 'Furniture',
subcategory: 'badge',
sale: 2009900,
profit: 699600,
},
{
category: 'Techonology',
subcategory: 'chair',
sale: 30000,
profit: 500,
},
{
category: 'Furniture',
subcategory: 'bed',
sale: 400,
profit: 200000,
},
]
The output should look like this:
{
"name": "data",
"children": [
{
"name": "Techonology",
"children": [
{
"name": "laptop",
"children": [
{
"name": "sale",
"value": 19000
}
]
},
{
"name": "chair",
"children": [
{
"name": "sale",
"value": 30000
}
]
}
]
},
{
"name": "Furniture",
"children": [
{
"name": "badge",
"children": [
{
"name": "sale",
"value": 2009900
}
]
},
{
"name": "bed",
"children": [
{
"name": "sale",
"value": 400
}
]
}
]
}
]
}
I assume you get the data from some site with this structure already define and this cannot be changed. Another way I suggest you to change the structure you're getting to something more manageable.
Anyway, following your structure or not, when you need to build a tree you must work recursively, to get the deepest structure and define correctly what you need.
Keep in mind this:
In your structure, you only have a subcategory per item but you can have a lot of entries with the same parent category where the only change is the subcategory element, this is not too optimal.
Since the structure becomes already defined, I think is better to treat and reorder it before arming the tree structure
To get a full example, I modified your data structure adding two children elements, because some of "subcategories" you have does not appear there.
So, the structure I used was :
var data1 = [
{
category: 'Techonology',
subcategory: 'laptop',
sale: 19000,
profit: 909049,
},
{
category: 'badge',
sale: 19000,
profit: 909049,
},
{
category: 'childrenchair',
sale: 19000,
profit: 909049,
},
{
category: 'chair',
subcategory: 'childrenchair',
sale: 19000,
profit: 909049,
},
{
category: 'Furniture',
subcategory: 'badge',
sale: 2009900,
profit: 699600,
},
{
category: 'Techonology',
subcategory: 'chair',
sale: 30000,
profit: 500,
},
{
category: 'Furniture',
subcategory: 'bed',
sale: 400,
profit: 200000,
},
]
and the code below defines the tree structure:
// loops the data to get all subcategory elements, this is used to
// avoid unnecessary iterations.
allChildren = data1.map(item=> item.subcategory).filter(item => !!item);
// this map has the data with the structure we need
preparedData = new Map()
data1.forEach(item => {
console.log(1, item.category, item.subcategory);
const data = preparedData.get(item.category) ?? {children: []};
data.isChildren = allChildren.includes(item.category);
if(item.subcategory) data.children.push(item.subcategory);
preparedData.set(item.category, Object.assign(item, data));
});
tree = [];
/**
getChildren method is recursive, it will be called for each category or subcategory with children's (recursively)
*/
getChildren = item=> {
const children = item.children.filter(item=> {
if(!preparedData.has(item)) return;
const data = preparedData.get(item);
const {category: name, sale, profit} = data;
subchildren = data.children.length ? getChildren(item) : [];
return {name, sale, profit, children: subchildren};
});
return children;
};
// loop the data
preparedData.forEach(item=>{
// it the item has the property isChildren as true, then is a subcategory and
// is not necessary to check it in the first level of the tree
if(item.isChildren) return;
const {category: name, sale, profit} = item;
children = item.children.length ? getChildren(item) : [];
tree.push({name, sale, profit, children});
});
I checked this code in the devtools console and runs, you can copy and pasted it to check and then make the adjusts required

Trying to map objects inside an object

I'm a beginner at webdev and I'm having a little problem with reactjs. I have an array of data about universities, like:
const data = [
{
name: "UFSM",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{name: "engineering", value: 10}, {name:"biology", value: 20}, ...]
},
{...
disciplines: [{name: "engineering", value: 7}, ....]
},...]
I'm using the map function to map all their names into an array, like:
let data_name = props.data.map((data) => data.name);
Is it possible to use map to group into an array the value of disciplines with the same name from different universities? Having an output like:
data_engineering = ["10", "7", ...]
So that I could have all engineering related disciplines from different universities on the same array.
const data = [{
name: "UFSM",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 10
}, {
name: "biology",
value: 20
}]
}, {
name: "ABCD",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 4
}, {
name: "biology",
value: 5
}]
}, {
name: "EFGH",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 5
}, {
name: "biology",
value: 9
}]
}]
let result = {};
data.map((data) => data.disciplines).forEach((item, index) => {
item.forEach((item, index) => {
if (!result[item.name]) {
result[item.name] = []
}
result[item.name].push(item.value);
});
});
console.log(JSON.stringify(result));
If you want an array for a single discipline like engineering, you can do the following map:
data_engineering =
props.data.map((uni) => uni.disciplines.engineering);
This array will have an undefined value for a university that doesn't have an engineering value defined. But that is probably what you want, because it means the indices line up with the indices of data_name.
If you want to build such an array for all disciplines, you can do the following:
const dataDiscipline = {};
// 1. Collect all disciplines from all universities
// by making them a key of dataDiscipline.
props.data.forEach((uni) =>
uni.disciplines.forEach((discipline) =>
dataDiscipline[discipline] = null
)
);
// 2. Now build the discipline array for each discipline.
Object.keys(dataDiscipline).forEach((discipline) =>
dataDiscipline[discipline] =
props.data.map((uni) => uni.disciplines[discipline])
);
// You can now use e.g. dataDiscipline.engineering

Group array of objects by common key (date string)

I have an array as below.
const list = [
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}
]
And I want to sum number and count depends on same date. After that it should be merged in same date. Therefore I want to get the result as below.
const list = [{
totalNumber: 9,
totalCount: 6,
detail:[{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
}]
},
{
totalNumber: 8,
totalCount: 2,
detail:[
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}]
}
]
I know stackoverflow is not code writing site. But I made this from some data. But this is my last stage for completing my object. And I don't know how I can group like that. I'' appreciate if you help me to resolve this problem. Thank you so much for reading it.
To me, optimal approach is building Map (having necessary property as a key) with Array.prototype.reduce(), then extracting array of values (aggregated records) with Map.prototype.values():
const src = [{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-02",number:4,count:1},{date:"2020-01-02",number:4,count:1}],
groupped = [...src
.reduce((acc, {date, number, count}) => {
const group = acc.get(date)
if(group){
group.totalNumber += number
group.totalCount += count
group.detail.push({date, number, count})
} else {
acc.set(
date,
{
date,
totalNumber: number,
totalCount: count,
detail: [{
date,
number,
count
}]
}
)
}
return acc
}, new Map)
.values()
]
console.log(groupped)
.as-console-wrapper{min-height:100%;}

How to move specific key value inside another array object in javascript

I have array object obj how to move particular key value store inside another array items in javascript.
I would like to know how to pass particular key value inside another array in javascript
var obj = [
{
id:1,
store: "10",
items: [{name:"sample1", total: 20}, {name:"sample2", total: 10}] // add store
},
{
id:1,
store: "11",
items: [{name:"sample3", total: 10}, {name:"sample4", total: 10}] // add store
}
]
function newarray(obj){
return obj.map(e=>...e,e.items.map(i=>{...i,store: e.store })
}
Expected Output
[
{
id:1,
store: "10",
items: [{name:"sample1", total: 20, store: "10"}, {name:"sample2", total: 10, store: "10"}]
},
{
id:1,
store: "11",
items: [{name:"sample3", total: 10, store: "11"}, {name:"sample4", total: 10, store: "11"}]
}
]
You are almost in the right direction, just few changes need to be done in your map function to get expected output:
var obj = [ { id:1, store: "10", items: [{name:"sample1", total: 20}, {name:"sample2", total: 10}] }, { id:1, store: "11", items: [{name:"sample3", total: 10}, {name:"sample4", total: 10}] }];
var result = obj.map(val=>(val.items = val.items.map(p=>({...p,store:val.store})), val));
console.log(result);
You can use reduce and forech if you want to try a different approach
var obj = [
{
id:1,
store: "10",
items: [{name:"sample1", total: 20}, {name:"sample2", total: 10}]
},
{
id:1,
store: "11",
items: [{name:"sample3", total: 10}, {name:"sample4", total: 10}]
}
]
newob=obj.reduce((acc,curr)=>{
curr.items.forEach(x=>{
x.store=curr.store
})
return [...acc,{...curr}]
},[])
console.log(newob)

Categories

Resources