【问题标题】:Js: What is the most efficient way to sort an array of objects into sections?Js:将对象数组排序为部分的最有效方法是什么?
【发布时间】:2021-12-17 07:33:56
【问题描述】:

我有一大堆对象,类似于:

[
  {
    id: "some_id",
    timestamp: 12345
  },
  {
    id: "some_other_id",
    timestamp: 12347
  },
  {
    id: "some_id",
    timestamp: 12346
  },
  {
    id: "some_other_id",
    timestamp: 12348
  },
  ...
]

我想以某种方式对数组进行排序,数组中有对象的“部分”,具体取决于对象具有的 id。在每个部分内,对象应根据时间戳升序排序。部分本身也应根据部分的第一个时间戳进行排序。所以数组应该是这样的:

[
  // section: some_id
  {
    id: "some_id",
    timestamp: 12345
  },
  {
    id: "some_id",
    timestamp: 12348
  },
  // section: some_other_id, comes ofter some_id section because 12346 > 12345
  {
    id: "some_other_id",
    timestamp: 12346
  },
  {
    id: "some_other_id",
    timestamp: 12347
  },
  ...
]

也应该可以在函数中选择升序/降序。 现在我有这个:

elements.sort((a, b) => {
  if (a.id === b.id) {
    if (sortAscending) {
      return a.timestamp > b.timestamp ? -1 : 1;
    } else {
      return a.timestamp > b.timestamp ? 1 : -1;
    }
  } else {
    return a.id.localeCompare(b.id);
  }
})

但是,这不会正确地对部分进行排序。有什么想法吗?

【问题讨论】:

  • 什么不起作用?代码看起来正确
  • 哦,您希望这些部分按第一个 ts 排序,而不是 id .....
  • 是的,正是^^
  • 您可以考虑为此使用地图,对 id 进行哈希处理。
  • 这将需要两次 (O2N) 来按部分中的最低时间戳对部分进行排序,并且在每个部分中对项目进行排序,因为在您完成所有操作之前您不知道该值是什么这几项。您可以先建立一个部分索引,然后再进行一次排序,这会让您知道哪个部分应该在另一个之前。

标签: javascript node.js arrays sorting


【解决方案1】:

你不可能以一种方式做到这一点。由于您不知道最小值是多少,因此必须执行多个步骤。

一种方法是组合、排序,然后根据最低值进行排序。

const data = [
  {
    id: "a",
    timestamp: 4
  },
  {
    id: "b",
    timestamp: 3
  },
  {
    id: "a",
    timestamp: 2
  },
  {
    id: "b",
    timestamp: 1
  },
];

const x = data.reduce((a,o) => {
  a[o.id] = a[o.id] || [];
  a[o.id].push(o);
  return a;
}, {});

const v = Object.values(x);
v.forEach(x => x.sort((a,b) => a.timestamp > b.timestamp ? 1 : -1))
v.sort((a,b)=>a[0].timestamp > b[0].timestamp ? 1 : -1);
const sorted = v.flat();

console.log(sorted);

另一种方法是找到最低的,然后用那个排序。

const data = [{
    id: "a",
    timestamp: 4
  },
  {
    id: "b",
    timestamp: 3
  },
  {
    id: "a",
    timestamp: 2
  },
  {
    id: "b",
    timestamp: 1
  },
];

const smallest = data.reduce((a ,o) => {
  a[o.id] = Math.min(a[o.id] === undefined? Number.POSITIVE_INFINITY : a[o.id], o.timestamp);
  return a;
}, {});

data.sort((a,b) => {
  return a.id === b.id ? 
    (a.timestamp > b.timestamp ?  1 : -1)
    : smallest[a.id] > smallest[b.id] ?  1 : -1;
})

console.log(data);

【讨论】:

    【解决方案2】:

    已编辑:如果我正确理解您的需求,这是一种方法

    const data = [{
        id: "some_id",
        timestamp: 12348
      },
      {
        id: "some_other_id",
        timestamp: 12346
      },
      {
        id: "some_id",
        timestamp: 12345
      },
      {
        id: "some_other_id",
        timestamp: 12347
      },
      {
        id: "X_other_id",
        timestamp: 12343
      },
      {
        id: "other_id",
        timestamp: 12349
      }
    ]
    
    
    const groupById = (acc, item) => {
      acc[item.id] ? acc[item.id].push(item) : acc[item.id] = [item];
      return acc;
    };
    
    function sort(arr, bool) {
      const groups = Object.values(arr.reduce(groupById, {}))
        .map(group => group.sort((a, b) => bool ? a.timestamp - b.timestamp : b.timestamp - a.timestamp))
        .sort((a, b) => bool ? a[0].timestamp - b[0].timestamp : b[0].timestamp - a[0].timestamp);
      return groups.flat()
    }
    
    console.log(sort(data, ascending = true))

    【讨论】:

    • 这会按他们的id 对部分进行排序,而不是按他们的时间戳,这似乎是 OP 想要的。
    • 您在这里得到了误报,因为最低的时间戳也属于最低的字母 id,请将other_id 更改为x_other_id 以查看它。
    • @pilchard 你是对的。但现在已经解决了。
    • @jfriend00 它排序也不首先按时间戳,然后是 id
    • OP 没有说他们想按字母顺序对部分进行排序。他们希望部分按各自包含的最低(或最高)时间戳排序。阅读问题的文本。
    【解决方案3】:

    这将需要两次遍历数组,因为在您知道部分的顺序应该是什么之前,您无法按部分排序,并且在您查看所有部分并因此知道什么之前您不知道这一点每个部分的最低或最高时间戳(取决于您是进行升序还是降序排序)。

    因此,可能有意义的是进行第一遍收集每个部分的极值并将其存储到 Map 对象中,您可以将其用作部分排序索引。然后,您可以运行.sort()。如果部分相同,则按时间戳排序。如果节不同,则按节索引中的值排序。

    function sortByIdAndTimestamp(data, sortAscending = true) {
        // create a Map object where keys are id values and values are the extreme
        // timestamp for that id
        const extremeTimestamp = new Map();
        for (let item of data) {
            if (testExtreme(item.timestamp, extremeTimestamp.get(item.id), sortAscending)) {
                extremeTimestamp.set(item.id, item.timestamp);
            }
        }
        // now just do a dual key sort
        data.sort((a, b) => {
            let result;
            if (a.id === b.id) {
                // if id is the same, just sort by timestamp
                result = b.timestamp - a.timestamp;
            } else {
                // if id is not the same, sort by the extreme timestamp of the id
                result = extremeTimestamp.get(b.id) - extremeTimestamp.get(a.id);
            }
            if (sortAscending) {
                result = -result;
            }
            return result;
        });
        return data;
    }
    
    function testExtreme(val, extremeSoFar, sortAscending) {
        // determine if this id's timestamp is more extreme
        // than what we already have for that section
        return (extremeSoFar === undefined) || (sortAscending ?
            val < extremeSoFar :
            val > extremeSoFar);
    }
    
    const sampleData = [{
            id: "some_id",
            timestamp: 12345
        },
        {
            id: "some_other_id",
            timestamp: 123
        },
        {
            id: "some_id",
            timestamp: 12346
        },
        {
            id: "some_other_id",
            timestamp: 99999
        },
        {
            id: "yet_another_id",
            timestamp: 1
        },
        {
            id: "yet_another_id",
            timestamp: 90000
        },
    ];
    
    sortByIdAndTimestamp(sampleData, true);
    console.log(sampleData);

    注意:当您说要按升序或降序排序时,我假设您的意思是针对部分和时间戳。因此,升序排序将首先具有最低时间戳部分,然后每个部分中的项目将按时间戳从低到高排序。并且,降序排序将首先具有最高时间戳的部分,然后每个部分中的项目将按时间戳从高到低排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      • 2016-10-19
      • 2016-08-27
      相关资源
      最近更新 更多