库存的Javascript算法



我有一个问题,我已经解决了,但我觉得我的实现非常基础,肯定可以学习更好的方法。给定两个对象阵列,一个用于我的客户要求的销售,另一个用于向我的供应商购买,我需要能够下订单,并知道何时能够满足他们的需求。我只会使用一种产品,让它更简单。我对这种情况下的问题编码很陌生,所以我真的会朝着正确的方向努力。也许有一个我没有使用过的数据结构可以在这里有所帮助。

属性:"已创建":创建销售订单时"数量":客户想要的商品数量

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  
}];

属性:"接收":我们预计何时接收产品"数量":我们将收到多少

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
}];

到目前为止我的代码。我正在返回一个数组,用于达到销售额,它显示我何时能够满足它。我使用当前实现运行的问题是,我无法覆盖所有情况。

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)

我认为你的方法基本上还可以。我只想提一些意见/问题:

  • 股票总是从0开始可以吗?他们从来没有留下过?根据我的说法,它应该是分配函数的参数之一
  • 您应该处理购买不足以满足销售的情况(在您的示例数据集中就是这种情况(=>使用当前代码,while循环可以遍历purchases数组的最大允许索引并抛出异常
  • 如果销售可以在其创建日期之前完成,那么可用性日期应该在创建日期之前,还是应该强制到创建日期?(如果销售总是在过去,购买总是在未来,那么这个问题就没有意义了(

以下是我将如何解决这个问题:

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);
}

我会这样做。首先,我将创建一个包含typedateidquantity字段的事件的集合,然后按日期对它们进行排序。这个中间格式可能看起来像这样:

[
{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}
]

然后,我会通过检查每个事件,将这个事件列表折叠成具有onHandcompletedopen属性的结构。如果它是purchase,那么我们把它的数量加到onHand上。然后,我们循环遍历现有的打开事件(如果是sale,则加上当前事件(,如果quantity不大于onHand,则创建新条目以添加到现有的completed数组,如果太大,则添加到新的open数组。代码可能看起来像这样:

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}

我们折叠的这个结构作为我们的整体输出,类似于这样:

{
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}
]
}

但这表明情况有所改善。我们可以使这个函数可重入。我们可以保存输出,然后下次需要添加事件时,我们可以简单地通过新的销售和购买将其传递回函数。这会给你一个更新的列表。这似乎是一个方便的功能,添加起来也不再困难。唯一真正的API变化是,您现在在每个调用中传递当前值,并为初始调用传递一些默认值。这里有一个版本:

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}

最新更新