js + lodash + moment 数组根据过期时间排序,快过期的在前面,日期相同金额大的在前面;data=[{"expireDate": "2025-06-24T15:59:59.999Z", "discount":2},{"expireDate": "2025-06-24T15:59:59.999Z", "discount":3}]
时间: 2025-06-10 19:12:55 浏览: 7
<think>我们有一个对象数组,每个对象包含expireDate(过期时间)和discount(折扣金额)等属性。排序要求:1.首先按expireDate排序(快过期在前,也就是日期越早的越靠前)。注意:这里可能会有null或者无效的日期,我们需要考虑如何处理。2.当expireDate相同时,按discount降序排序(金额大的在前)。我们将使用Lodash的`_.orderBy`方法,因为它可以支持多条件排序,并且可以指定每个条件的排序顺序。同时,使用Moment.js来处理日期,以便比较日期的大小。步骤:1.对于expireDate,我们将其转换为moment对象,然后比较。注意:如果expireDate为null或无效,我们可以将其视为一个非常大的日期(这样它们会排在最后)或者一个非常小的日期(排在最前)。根据业务需求,通常我们希望无效日期排到最后。但这里题目要求“快过期在前”,所以无效日期应该排到后面(因为快过期是指日期临近现在,而无效日期我们无法判断,所以放在最后)。2.对于discount,直接按数值降序排序。在`_.orderBy`中,我们可以指定一个迭代函数来处理日期,将日期字符串转换为一个可排序的值(比如时间戳)。对于无效的日期,我们返回一个很大的值(这样按升序排列时会排到最后)或者一个很小的值。但是这里我们要升序排列(快过期的在前,即日期小的在前),所以对于无效日期,我们希望它们排后面,所以返回一个很大的值(如Infinity),这样在升序排列时它们就在最后。注意:如果日期相同,再按照discount降序排列。因此,我们设置两个排序条件:条件1:expireDate,使用自定义迭代器,将日期字符串转为时间戳(如果无效则返回Infinity),然后升序排列(小的在前)。条件2:discount,直接使用属性值,降序排列(desc)。另外,我们也可以使用Lodash的`_.sortBy`,但它只能都升序或都降序,而`orderBy`可以分别指定顺序。具体代码:使用_.orderBy(array,[iteratees],[orders])其中iteratees可以是一个函数数组,orders是一个字符串数组('asc'或'desc')假设数组为arr,每个元素为obj,obj.expireDate是日期字符串(如'2022-12-31'),obj.discount是数字。我们将这样写:_.orderBy(arr,[(obj)=>{constdate=moment(obj.expireDate,"YYYY-MM-DD");//根据实际格式解析if(date.isValid()){returndate.valueOf();//时间戳}else{returnInfinity;//无效日期返回无穷大,这样升序时会排到最后}},'discount'],['asc',//第一个条件升序(因为时间戳小表示早,快过期,所以升序则早的在前)'desc'//第二个条件降序]);注意:如果expireDate是null、undefined或者无效字符串,我们都返回Infinity。另一种考虑:如果业务要求无效日期当作已经过期,那么应该排在最前面?但题目要求是快过期在前,已经过期的其实也是快过期(已经过期了,当然应该排在前面)。所以,这里需要明确业务要求。如果已经过期,那么它应该比未来的日期更“快过期”。所以,我们应该将已经过期的排在前面,而无效日期排在最后。但是,注意:题目没有明确说明过期日期的含义。根据常识,已经过期的产品应该被优先处理,所以它们应该排在前面。因此,我们需要判断:如果日期是无效的,返回Infinity(排最后)。如果日期是有效的,但已经过期(即比当前时间早),那么它们应该比未来的日期更靠前吗?注意,题目要求是“快过期在前”,这个“快过期”应该包括已经过期的。所以,我们不需要区分是否已经过期,只需要按日期的时间戳从小到大排序(升序)即可,因为已经过期的日期时间戳一定比未来的小,所以升序排列时,已经过期的会排在前面。所以,我们直接按照日期时间戳升序即可。但是,如果两个日期都是无效的怎么办?那么它们的第一个比较值都是Infinity,然后进入第二个比较条件,按discount降序排列。因此,我们使用上述方法。代码示例:假设数组结构为:letarr=[{id:1,expireDate:'2022-01-01',discount:100},{id:2,expireDate:null,discount:200},{id:3,expireDate:'2022-02-01',discount:150},{id:4,expireDate:'2022-01-01',discount:200},{id:5,expireDate:'invalid',discount:300},];按照上面的排序:首先,id为1和4的expireDate相同(都是2022-01-01),然后它们内部按discount降序,所以id4(200)排在id1(100)前面。然后,id3的expireDate是2022-02-01,比2022-01-01晚,所以排在1和4后面。然后,id2和5的日期无效,它们都返回Infinity,所以这两个排在最后。然后这两个之间再按discount降序:id5(300)和id2(200),所以id5排在id2前面吗?不对,因为discount降序,所以id5(300)比id2(200)大,所以id5排在id2前面。注意:我们的第二个条件是'discount'降序,所以在第一个条件相同(都是Infinity)的情况下,id5的discount是300,id2是200,所以id5排在id2前面。整个顺序为:id4,id1,id3,id5,id2。如果希望无效日期中,discount大的排前面,那么就这样。这符合要求。但是,这里有一个问题:如果两个对象都是无效日期,那么按discount降序排列,这符合第二个条件。所以正确。代码实现:注意:我们使用moment来解析日期。如果日期格式不是标准的,可能需要调整格式字符串。另外,如果expireDate已经是JavaScript的Date对象,那么可以直接用,但我们这里假设是字符串。如果日期字符串是ISO格式(如'2022-01-01'),moment可以自动解析,否则需要指定格式。下面我们写一个函数来实现:</think>
要使用 JavaScript、Lodash 和 Moment.js 实现对象数组按过期时间(快过期在前)和折扣金额(降序)的双重排序,可以按照以下步骤操作:
### 完整解决方案
```javascript
// 导入所需的库
const _ = require('lodash');
const moment = require('moment');
// 示例数据
const items = [
{ id: 1, expireDate: '2023-12-31', discount: 100 },
{ id: 2, expireDate: '2023-06-15', discount: 200 },
{ id: 3, expireDate: '2023-06-15', discount: 150 },
{ id: 4, expireDate: null, discount: 300 }, // 无过期日期的情况
{ id: 5, expireDate: '2023-09-01', discount: 250 },
];
// 核心排序逻辑
const sortedItems = _.orderBy(items, [
// 第一排序条件:过期时间(升序,快过期在前)
item => {
return item.expireDate
? moment(item.expireDate).valueOf() // 有效日期转为时间戳
: Number.MAX_SAFE_INTEGER; // 无效/空日期处理为最大值
},
// 第二排序条件:折扣金额(降序)
item => item.discount
], ['asc', 'desc']); // 指定排序方向
console.log(sortedItems);
```
### 关键说明
1. **过期时间排序逻辑**:
- 使用 `moment(expireDate).valueOf()` 将日期转为 UNIX 时间戳(毫秒数)
- **特殊处理**:空值或无效日期使用 `Number.MAX_SAFE_INTEGER`,确保排在最末尾
- 按升序(`asc`)排列实现"快过期在前"
2. **折扣金额排序**:
- 直接使用数值进行降序(`desc`)排序
- 当日期相同时,此条件生效
3. **双重排序机制**:
- `_.orderBy()` 支持多条件排序
- 排序条件数组和方向数组需保持顺序对应:
```javascript
[条件1, 条件2] 对应 [方向1, 方向2]
```
### 处理边界情况
```javascript
// 增强版日期处理(添加格式验证)
const getValidDate = (dateStr) => {
const date = moment(dateStr);
return date.isValid() ? date.valueOf() : Number.MAX_SAFE_INTEGER;
};
// 在排序条件中使用:
item => getValidDate(item.expireDate)
```
### 排序效果示例
输入数据:
```javascript
[
{id:1, expireDate:'2023-12-31', discount:100},
{id:2, expireDate:'2023-06-15', discount:200},
{id:3, expireDate:'2023-06-15', discount:150},
{id:4, expireDate:null, discount:300},
{id:5, expireDate:'2023-09-01', discount:250}
]
```
输出顺序:
1. 2023-06-15(discount:200)
2. 2023-06-15(discount:150) ← 相同日期按金额降序
3. 2023-09-01(discount:250)
4. 2023-12-31(discount:100)
5. null(discount:300) ← 无效日期排最后
### 典型应用场景
这种排序适用于:
1. 电商促销商品管理(优先展示即将过期的高折扣商品)
2. 优惠券/代金券排序系统
3. 库存管理系统(临近保质期商品优先处理)
> 该方法参考了排序条件组合的实现方式,类似引用中提到的字段名+排序方向的参数化处理[^2]
阅读全文
相关推荐


















