首页 > 解决方案 > 按多个属性对对象数组进行排序,包括嵌套数组中的对象

问题描述

我知道在这里多次问过类似的问题,但在我的情况下有些东西不起作用。我有一组具有以下结构的对象:

const items = [
  {id: 100, title:'text', path: '1', readMessages: [
      {text: 'foo', InsertDate: Moment}
    ], unreadMessages: [
      {text: 'bar', InsertDate: Moment}, {text: 'baz', InsertDate: Moment}
    ]},
  {id: 103, title:'else', path: '1.1', readMessages: [], unreadMessages: [
    {text: 'cux', InsertDate: Moment},
    {text: 'dux', InsertDate: Moment},
    {text: 'tux', InsertDate: Moment}
  ]},
  {id: 110, title:'another', path: '2', readMessages: [], unreadMessages: []}
]

我需要对这个数组进行以下排序:

需要考虑的重要事项:readMessages 和 unreadMessages 数组可能为空(其中之一或两者),InsertDate 是 Moment 对象,路径可能有 '1'、'1.1' '1.1.1' 结构。

最终,具有最多 unreadMessages 的对象应该位于列表顶部,然后是没有 unreadMessages 但具有 readMessages 的对象,然后是两个空数组。所有必须首先按消息数量(首先是大多数消息)排序,然后按 InsertDate(首先是较旧消息),然后按路径(升序 - 1、1.1、1.2、1.2.1、1.2.2、1.3、2 等) .

我试图做这样的事情:

const sortedItems = items.sort((a, b) => {
  if (a.unreadMessages.length === b.unreadMessages.length) {
    if (a.unreadMessages.length) {
      if (a.unreadMessages[a.unreadMessages.length - 1].InsertDate === b.unreadMessages[a.unreadMessages.length - 1].InsertDate) {
        return (a.path < b.path) ? -1 : (a.path > b.path) ? 1 : 0;
      } else {
          return (a.unreadMessages[a.unreadMessages.length - 1].InsertDate < b.unreadMessages[b.unreadMessages.length - 1].InsertDate) ? -1 : 1;
      }
    }
    else if (a.readMessages.length) {
      if (a.readMessages[a.readMessages.length - 1].InsertDate === b.readMessages[a.readMessages.length - 1].InsertDate) {
        return (a.path < b.path) ? -1 : (a.path > b.path) ? 1 : 0;
      } else {
          return (a.unreadMessages[a.unreadMessages.length - 1].InsertDate < b.unreadMessages[b.unreadMessages.length - 1].InsertDate) ? -1 : 1;
      }
    }
  } else {
    return (a.unreadMessages.length < b.unreadMessages.length) ? -1 : 1;
  }
}).reverse()

一方面,它确实将具有最多未读消息的对象放在首位,但它不按 InsertTime/path 排序。当我将其中一个问题标记为已读(将所有对象从 unreadMessages 移动到 readMessages)时,该对象出现在列表的最底部,在两个数组都为空的对象下方。

我认为按 Moment 对象排序可能有问题,所以我尝试了类似的方法moment(a.readMessages[a.readMessages.length - 1].InsertDate),但这没有帮助。

请问有什么建议吗?

标签: javascriptarraysecmascript-6

解决方案


const items=[
    {id:100,title:"text",path:"1",readMessages:[{id:1,text:"foo",InsertDate:"2020-07-19T07:14:01.000Z"}],unreadMessages:[{id:5,text:"bar",InsertDate:"2020-07-22T07:03:13.000Z"},{id:6,text:"baz",InsertDate:"2020-07-22T07:03:24.000Z"}]},
    {id:103,title:"else",path:"1.1",readMessages:[],unreadMessages:[{id:3,text:"dux",InsertDate:"2020-07-22T07:02:57.000Z"},{id:4,text:"cux",InsertDate:"2020-07-22T07:03:06.000Z"},{id:2,text:"tux",InsertDate:"2020-07-23T10:35:40.000Z"}]},
    {id:110,title:"another",path:"2",readMessages:[],unreadMessages:[]},
    {id:111,title:"another",path:"3",readMessages:[],unreadMessages:[{id:7,text:"tux",InsertDate:"2020-07-21T10:35:40.000Z"}]},
    {id:112,title:"another",path:"4",readMessages:[],unreadMessages:[{id:8,text:"tux",InsertDate:"2020-07-21T10:35:40.000Z"}]},
    {id:113,title:"another",path:"5",readMessages:[],unreadMessages:[{id:2,text:"tux",InsertDate:"2020-07-20T10:35:40.000Z"},{id:2,text:"lux",InsertDate:"2020-07-21T10:35:40.000Z"}]}
];

const getLastMessage = item => {
    const messages = [...item.readMessages, ...item.unreadMessages].sort((a, b) =>
        a.InsertDate < b.InsertDate ? 1 : -1,
    );

    return messages[0];
};

const sortedItems = items.sort((a, b) => {
    // (1) Amount of unreadMessages [desc]
    if (a.unreadMessages.length > b.unreadMessages.length) {
        return -1;
    } else if (a.unreadMessages.length < b.unreadMessages.length) {
        return 1;
    }

    // (2) Time passed since last message [desc]
    const aLastMessage = getLastMessage(a);
    const bLastMessage = getLastMessage(b);

    // Checking the emptiness of one of the message arrays
    if (aLastMessage && !bLastMessage) {
        return -1;
    } else if (!aLastMessage && bLastMessage) {
        return 1;
    }

    if (aLastMessage.InsertDate > bLastMessage.InsertDate) {
        return -1;
    } else if (aLastMessage.InsertDate < bLastMessage.InsertDate) {
        return 1;
    }

    // (3) Path [asc]
    if (a.path > b.path) {
        return 1;
    } else if (a.path < b.path) {
        return -1;
    }

    return 0;
});

console.log(
    'Sorted ids:',
    sortedItems.map(i => i.id),
);
console.log(sortedItems);


推荐阅读