最近在写网络报价的时候,遇到一个需求,就是要根据采购数量,找到符合数量的阶梯区间,并找到最便宜的采购价格。
比如下面:
let originViewList = [
{
id:1,
incrementalQuantity:10,
priceList:[
{
minQuantity:1,
price:20
},
{
minQuantity:1,
price:18
},
{
minQuantity:300,
price:17
}
]
}
]
上面数据中的incrementalQuantity
是增长量,minQuantity
是阶梯数,比如采购的数量是200,则可以选择minQuantity
为1的,然后增长量是10的,这样,200 = 1+1020;则最后的采购量是201。如果选择minQuantity
为300,则最低采购量只能是300了。两者价格比较:20120与300*17相比较,还是选择minQuantity
为1的采购价格更低,而且minQuantity
为1的价格为18的采购价更低,所以最后选择的是{minQuantity:1,price:18}
的一组。
所以要实现的效果:
1.根据阶梯数+增长量:获取最终的采购量,阶梯数低于采购量的,需要配合增长量。阶梯数高于采购量的,直接使用阶梯数作为采购量
2.如果阶梯数有相同的,则需要找个价格最低的
下面是我的解决思路:
解决步骤1:根据采购量找到介于哪两个阶梯价之间
findNearestNumber(arr, target){
return arr.reduce((pre, curr) => {
return Math.abs(pre - target) > Math.abs(curr - target) ? curr : pre;
})
},
使用方法:
let numArr = [1,1,20,30];
let num = 12;
let nearNum = this.findNearestNumber(numArr, num);
let nearIndex = numArr.indexOf(nearNum);
if (numArr[nearIndex] > num) {
nearIndex = nearIndex - 1 > -1 ? nearIndex - 1 : 0;
}
console.log(nearIndex);
也就是说12最接近的索引是1,也就是介于索引1到索引2之间。
再看下数量是2的,最接近的索引是0,索引1也是最接近的,这种相同阶梯数的后面有处理。
解决步骤2:根据阶梯数+增长量获取最终的采购量
let arr = originViewList[0].priceList;
let resultNum = Math.ceil((num - arr[nearIndex].minQuantity) / this.originViewList[index].incrementalQuantity) * this.originViewList[index].incrementalQuantity + arr[nearIndex].minQuantity;
console.log('numArr', numArr, nearIndex, resultNum);
上面数据中的incrementalQuantity
是增长量,minQuantity
是阶梯数,比如采购的数量是200,则可以选择minQuantity
为1的,然后增长量是10的,这样,200 = 1+10*20;则最后的采购量是201。如果选择minQuantity
为300,则最低采购量只能是300了。
解决步骤3:如果阶梯数有相同,则需要根据价格排序,找最低价的
//如果有数量相同的情况
let eqIndexArr = [];
numArr.forEach((n, nIndex) => {
if (n == numArr[nearIndex]) {
eqIndexArr.push(nIndex);
}
})
if (eqIndexArr.length > 1) {
let eqPriceArr = [];
eqIndexArr.forEach((eq) => {
eqPriceArr.push({ index: eq, price: arr[eq].price });
})
const sortBy = (field) => {
return function (a, b) {
return a[field] - b[field];
};
}
let maxArr = eqPriceArr.sort(sortBy('price'));//对象数组根据price价格字段升序排列
this.OptimalIndex[index] = maxArr[0].index;
res = resultNum * maxArr[0].price;//采购量*最低价格
}
解决步骤4:如果没有相同阶梯价的,则直接计算
//当前阶梯价*采购量与下一个的最小数量*价格相比较,谁的采购价低,选择谁
if (resultNum * arr[nearIndex].price < arr[nearIndex + 1].minQuantity * arr[nearIndex + 1].price) {
this.OptimalIndex[index] = nearIndex;
res = resultNum * arr[nearIndex].price;
} else {
this.OptimalIndex[index] = nearIndex + 1;
res = arr[nearIndex + 1].minQuantity * arr[nearIndex + 1].price;
}
上面的两种情况都能拿到最终的采购总价了。下面根据采购总价进行排序:
//sortLevel 是升序还是降序的判断
priceSort(num) {
var _that = this;
this.viewList.forEach((item, index) => item.Index = index);
this.originViewList = JSON.parse(JSON.stringify(this.viewList));
if (this.viewList.length == 1) {
_that.findPriceListPrice(this.viewList[0].priceList, num, this.viewList[0].Index);
} else {
this.viewList.sort(function (a, b) {
//找index a.priceList b.priceList
var aPrice = _that.findPriceListPrice(a.priceList, num, a.Index);
var bPrice = _that.findPriceListPrice(b.priceList, num, b.Index);
return !_that.sortLevel ? (aPrice - bPrice) : (bPrice - aPrice);
})
}
},
合并代码记录:
最终代码记录:
priceSort(num) {
var _that = this;
this.viewList.forEach((item, index) => item.Index = index);
this.originViewList = JSON.parse(JSON.stringify(this.viewList));
if (this.viewList.length == 1) {
_that.findPriceListPrice(this.viewList[0].priceList, num, this.viewList[0].Index);
} else {
this.viewList.sort(function (a, b) {
//找index a.priceList b.priceList
var aPrice = _that.findPriceListPrice(a.priceList, num, a.Index);
var bPrice = _that.findPriceListPrice(b.priceList, num, b.Index);
return !_that.sortLevel ? (aPrice - bPrice) : (bPrice - aPrice);
})
}
},
findPriceListPrice(arr, num, index) {
var res = 99999999;
let numArr = arr.map(a => a.minQuantity);
if (!num) {
res = 99999999;
} else {
let nearNum = this.findNearestNumber(numArr, num);
let nearIndex = numArr.indexOf(nearNum);
if (numArr[nearIndex] > num) {
nearIndex = nearIndex - 1 > -1 ? nearIndex - 1 : 0;
}
let resultNum = Math.ceil((num - arr[nearIndex].minQuantity) / this.originViewList[index].incrementalQuantity) * this.originViewList[index].incrementalQuantity + arr[nearIndex].minQuantity;
console.log('numArr', numArr, nearIndex, resultNum);
if (nearIndex < arr.length - 1) {
//如果有数量相同的情况
let eqIndexArr = [];
numArr.forEach((n, nIndex) => {
if (n == numArr[nearIndex]) {
eqIndexArr.push(nIndex);
}
})
if (eqIndexArr.length > 1) {
let eqPriceArr = [];
eqIndexArr.forEach((eq) => {
eqPriceArr.push({ index: eq, price: arr[eq].price });
})
const sortBy = (field) => {
return function (a, b) {
return a[field] - b[field];
};
}
let maxArr = eqPriceArr.sort(sortBy('price'));
this.OptimalIndex[index] = maxArr[0].index;
res = resultNum * maxArr[0].price;
} else {
if (resultNum * arr[nearIndex].price < arr[nearIndex + 1].minQuantity * arr[nearIndex + 1].price) {
this.OptimalIndex[index] = nearIndex;
res = resultNum * arr[nearIndex].price;
} else {
this.OptimalIndex[index] = nearIndex + 1;
res = arr[nearIndex + 1].minQuantity * arr[nearIndex + 1].price;
}
}
} else {
this.OptimalIndex[index] = nearIndex;
res = resultNum * arr[nearIndex].price;
}
}
return res;
},
findNearestNumber(arr, target){
return arr.reduce((pre, curr) => {
return Math.abs(pre - target) > Math.abs(curr - target) ? curr : pre;
})
},
上面的代码逻辑其实是有问题的,最正确的解决办法就是:根据每一个的阶梯数,获取最终的采购量,然后与各自的价格相乘,找最低的作为最终的采购价。
这样才是最正确的。
下面附上最正确的解决办法:
findPriceListPrice(list, num, index) {
var res = 99999999;
let arr = JSON.parse(JSON.stringify(list));
arr && arr.forEach((a, aIndex) => {
a.index = aIndex;
})
console.log('arr', arr);
if (!num) {
res = 99999999;
} else {
arr && arr.forEach(item => {
if (item.minQuantity < num) {
item.resultNum = Math.ceil((num - item.minQuantity) / this.originViewList[index].incrementalQuantity) * this.originViewList[index].incrementalQuantity + item.minQuantity;
} else {
item.resultNum = item.minQuantity;
}
item.totalPrice = item.resultNum * item.price;
})
const sortBy = (field) => {
return function (a, b) {
return a[field] - b[field];
};
}
let maxArr = arr.sort(sortBy('totalPrice'));
console.log('maxArr', maxArr);
this.OptimalIndex[index] = maxArr[0].index;
res = maxArr[0].totalPrice;
}
return res;
},