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}
Related
I have an array of data like so:
[
{id: 1, date: "2022-10-01T12:00:00.00", type: 1},
{id: 2, date: "2022-10-02T12:00:00.00", type: 1},
{id: 3, date: "2022-10-03T12:00:00.00", type: 2},
{id: 4, date: "2022-10-04T12:00:00.00", type: 2},
]
I'd like to filter this so that I get an array of objects that only includes objects with the most recent date for each type. So something like this:
[
{id: 2, dttm: "2022-10-02T12:00:00.00", type: 1},
{id: 4, dttm: "2022-10-04T12:00:00.00", type: 2},
]
I suspect there's a clever way to do this with the .reduce() function, but I haven't quite figured that out yet.
It's easy to compare dates this way just using string comparing since they are sorted by year-month-date. As for the rest, yes reduce is an option but basically it's just a loop over the array grouping by type.
var arr = [
{id: 1, date: "2022-10-01T12:00:00.00", type: 1},
{id: 2, date: "2022-10-02T12:00:00.00", type: 1},
{id: 3, date: "2022-10-03T12:00:00.00", type: 2},
{id: 4, date: "2022-10-04T12:00:00.00", type: 2},
];
var grouped = arr.reduce(function(agg, item) {
agg[item.type] = agg[item.type] || {
id: item.id,
dttm: item.date,
type: item.type
};
if (item.date > agg[item.type].dttm) {
agg[item.type].id = item.id
agg[item.type].dttm = item.date
}
return agg;
}, {})
console.log (Object.values(grouped))
We can achieve this using .reduce by keeping a running tally of the most recent items.
Note:
Dates can be compared in js as their valueOf method is equivalent to someDate.getTime() which gives a nice integer for comparison.
let items = [{
id: 1,
date: "2022-10-01T12:00:00.00",
type: 1
},
{
id: 2,
date: "2022-10-02T12:00:00.00",
type: 1
},
{
id: 3,
date: "2022-10-03T12:00:00.00",
type: 2
},
{
id: 4,
date: "2022-10-04T12:00:00.00",
type: 2
},
]
let recentItems = Object.values(items.reduce((recent, item) => {
debugger;
if (
// type has not items yet
!recent[item.type]
// the current item is more recent
||
new Date(recent[item.type].date) < new Date(item.date)
) {
recent[item.type] = item
}
return recent;
}, {}))
console.log(recentItems)
This does what you specify. (See in-code comments for more info.)
// Calls filter function on the data array and prints output
const data = getData();
console.log(recentOfEachType(data));
// Defines filter function
function recentOfEachType(arr){
// Sets up output array
let recents = [];
// Loops through data to populate output
for(const item of arr){
// Gets position of existing item with this type
const typeIndex = recents.findIndex(recent => recent.type === item.type);
// If no item with this type has been added yet
if(typeIndex < 0){
recents.push(item);
}
// If a newer item is found (ignoring items with identical timestamps)
else if(item.date > recents[typeIndex].date){
recents[typeIndex] = item;
}
// (else do nothing)
}
return recents;
}
// Creates oringal data array
function getData(){
return [
{id: 1, date: "2022-10-01T12:00:00.00", type: 1},
{id: 2, date: "2022-10-02T12:00:00.00", type: 1},
{id: 3, date: "2022-10-03T12:00:00.00", type: 2},
{id: 4, date: "2022-10-04T12:00:00.00", type: 2}
];
}
The first array is:
[
{
id: "megaphone",
name: "Megaphone",
kind: "Consumable",
description: "Unmutes you if you are currently muted",
price: 10,
},
{
id: "expcharge",
name: "Exp Charge",
kind: "Consumable",
description: "Double exp for an hour",
price: 50,
},
{
id: "commonlootbox",
name: "Common Lootbox",
kind: "Consumable",
description: "Chance for a random amount of coins or an item",
price: 1,
},
];
The second part of the JSON I need is into a JSON file.
I tried to add the data of the json like this:
arr.forEach((a) => {
no.push(shop.find((i) => i.id === a.name));
});
And this is perfect, because i get only the data i need.
But the result is that i have two arrays now.
The second is:
[
{ name: "megaphone", quantity: 5 },
{ name: "expcharge", quantity: "3" },
{ name: "commonlootbox", quantity: "3" },
];
Now, what I need to do is basically for Each object in the array, I need to do
"array1.name - array2.quantity (array1.description)"
I need to post it like
Megaphone - 5 (Unmutes you if you are currently muted)
Common Lootbox - 3 (Double exp for an hour)
etc.
Hope this is clear.
Maybe my method is wrong?
well, you need to itirate over the second array and make strings of the result that you want, like so
const secondArray = [
{name: 'megaphone', quantity: 5}
{name: 'expcharge', quantity: '3'}
{name: 'commonlootbox', quantity: '3'}
]
const desiredarray = secondArray.map(item => {
const matchedItem = data.find(({ id }) => id == item.name)
return `${matchedItem.name} - ${item.quantity} (${matchedItem.description})`
});
I think you could store one or both arrays in a Map or directly in an object.
You could try something like this:
obj = array.reduce((obj, element) => {
obj[element.id] = element;
return obj;
}, {});
Now you can iterate one of the arrays and access the corresponding value in the other one directly.
Ciao, you could do something like this:
Solution with array of JSON:
const arr1 = [{id: 'megaphone', name: 'Megaphone', kind: 'Consumable', description: 'Unmutes you if you are currently muted', price: 10},
{id: 'expcharge', name: 'Exp Charge', kind: 'Consumable', description: 'Double exp for an hour', price: 50},
{id: 'commonlootbox', name: 'Common Lootbox', kind: 'Consumable', description: 'Chance for a random amount of coins or an item', price: 1}];
const arr2 = [{name: 'megaphone', quantity: 5},
{name: 'expcharge', quantity: '3'},
{name: 'commonlootbox', quantity: '3'}];
let arr3 = arr1.map(item1 => {
let ok_el = {};
arr2.map(item2 => {
if (item1.id === item2.name) {
ok_el.id = item1.id;
ok_el.name = item1.name;
ok_el.quantity = item2.quantity;
ok_el.description = item1.description;
return ok_el;
}
});
return ok_el;
});
let result = arr3.map(el => {
return el.name + " - " + el.quantity + " (" + el.description + ")";
});
console.log(result);
Solution with JSON of JSONs:
const arr1 = {0:{id: 'megaphone', name: 'Megaphone', kind: 'Consumable', description: 'Unmutes you if you are currently muted', price: 10},
1:{id: 'expcharge', name: 'Exp Charge', kind: 'Consumable', description: 'Double exp for an hour', price: 50},
2:{id: 'commonlootbox', name: 'Common Lootbox', kind: 'Consumable', description: 'Chance for a random amount of coins or an item', price: 1}};
const arr2 = {0:{name: 'megaphone', quantity: 5},
1:{name: 'expcharge', quantity: '3'},
2:{name: 'commonlootbox', quantity: '3'}};
let arr3 = Object.values(arr1).map(item1 => {
let ok_el = {};
Object.values(arr2).map(item2 => {
if (item1.id === item2.name) {
ok_el.id = item1.id;
ok_el.name = item1.name;
ok_el.quantity = item2.quantity;
ok_el.description = item1.description;
return ok_el;
}
});
return ok_el;
});
let result = arr3.map(el => {
return el.name + " - " + el.quantity + " (" + el.description + ")";
});
console.log(result);
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
}
}
I have to array of objects which can be related to each other through CategoryId. like below code:
const Category = [
{ ID: 100 , Description: Cate1},
{ID: 101 , Description: Cate2}
]
const Items = [
{ID: 2001, CategoryID: 100, Desc: Item1 },
{ID: 2002, CategoryID: 100, Desc: Item2 },
{ID: 2003, CategoryID: 101, Desc: Item3 },
]
I am going to distribute items by the use of map method in lis that are summarized in their own ul according to the category descriptions that are extracted from category id which exists in both arrays. For example:
Cate1 > ul
item1 > li
item2 > li
Cate2 > ul
item3 > li
.
.
.
How can I tackle this issue with Javascript or ES6/7/8?
You can look at each category and for each one filter() the matching items. This is easy, but will be a little slow if you have lots of data because you loop though the item list every time:
const Category = [{ ID: 100 , Description: 'Cate1'}, {ID: 101 , Description: 'Cate2'}]
const Items = [ {ID: 2001, 'CategoryID': 100, Desc: 'Item1' }, {ID: 2002, 'CategoryID': 100, Desc: 'Item2' }, {ID: 2003, 'CategoryID': 101, Desc: 'Item3' }, ]
Category.forEach(cat => {
console.log('ul> '+cat.Description)
Items.filter(item => item.CategoryID === cat.ID)
.forEach(item => console.log(' li> ' + item.Desc))
})
Alternatively you can build a lookup table for your items base on ID once, and then use it to find the items in constant time. This will be faster with larger data, but requires a little more work upfront:
const Category = [{ ID: 100 , Description: 'Cate1'}, {ID: 101 , Description: 'Cate2'}]
const Items = [ {ID: 2001, 'CategoryID': 100, Desc: 'Item1' }, {ID: 2002, 'CategoryID': 100, Desc: 'Item2' }, {ID: 2003, 'CategoryID': 101, Desc: 'Item3' }, ]
let lookup = Items.reduce((a, c) => {
(a[c.CategoryID] || (a[c.CategoryID] = [])).push(c)
return a
}, {})
Category.forEach(cat => {
console.log('ul> '+cat.Description)
lookup[cat.ID]
.forEach(item => console.log(' li> ' + item.Desc))
})
I am working on a Nodejs project. I have to create a function which takes an object (a child category) like:
{
id: 65,
name: 'Outdoor',
parent_id: 2
}
Now I want my function to check for the parent category by using parent_id from database and return an array/object like this:
{
id: 2,
name: 'Furniture',
parent: {
id: 1,
name: 'Residential',
parent: {
id: ...,
name: ...,
parent: {
and so on..
}
}
}
}
This is what I have done so far:
* _get_category_parents(category, _array) {
if(_array === undefined) _array = []
if( category.parent_id !== 0 ) {
const c_parent = yield this.Database.from('categories').where('id', '=', category.parent_id)
_array.push({id: c_parent[0].id, name: c_parent[0].name})
yield this._get_category_parents(c_parent[0], _array)
}
return _array
}
And calling this function like this:
const parents = yield this._get_category_parents(category)
This returns me an array of parents like this:
[
{
"id": 2,
"name": "Furniture"
},
{
"id": 1,
"name": "Residential"
}
]
I want Residential object to be appended in Furniture's parent node.
I have spent too much time on this but not getting what I want. Any help would be deeply appreciated.
What you want to think about is a recursive solution.
Since you're calling a database, it's probably unlikely, but if the lookup by id is synchronous, you might do it with code something like the following (note that I'm faking a db here):
const getHierarchy = (lookup, child) => {
const {id, name, parent_id} = lookup(child) ||
{id: null, name: null, parent_id: 0}
return parent_id == 0
? {id, name, parent_id}
: {...{id, name}, ...{parent: getHierarchy(lookup, {parent_id})}}
}
const items = [
{id: 1, name: 'Residential', parent_id: 5},
{id: 2, name: 'Furniture', parent_id: 1},
{id: 3, name: 'Other', parent_id: 0},
{id: 4, name: 'FooBar', parent_id: 3},
{id: 5, name: 'Stuff', parent_id: 0}
]
const lookup = child => items.find(item => item.id == child.parent_id)
const item = {id: 65, name: 'Outdoor', parent_id: 2}
console.log(getHierarchy(lookup, item))
You would have to write an appropriate lookup function, presumably using this.Database.from(...). You might also want to simplified version that bakes in your lookup function, in which case, you might write
const getAncestry = (item) => getHierarchy(lookup, item)
If, as seems more likely, your lookup is asynchronous, then that will affect getHierarchy and how you call it. Here's one possibility:
const getHierarchy = async (lookup, child) => {
const {id, name, parent_id} = await lookup(child) ||
{id: null, name: null, parent_id: 0}
return parent_id == 0
? {id, name, parent_id}
: {...{id, name}, ...{parent: await getHierarchy(lookup, {parent_id})}}
}
const items = [
{id: 1, name: 'Residential', parent_id: 5},
{id: 2, name: 'Furniture', parent_id: 1},
{id: 3, name: 'Other', parent_id: 0},
{id: 4, name: 'FooBar', parent_id: 3},
{id: 5, name: 'Stuff', parent_id: 0}
]
const lookup = async child => new Promise(
(resolve, reject) => setTimeout(
() => resolve(items.find(item => item.id == child.parent_id)),
1000
)
)
const getAncestry = async item => getHierarchy(lookup, item)
const item = {id: 65, name: 'Outdoor', parent_id: 2}
getAncestry(item).then(console.log)
Note the change in how you call the function. You need to call .then() on the resulting promise to get any useful behavior.