4

有没有一种方法可以有效地实现按功能分组而不发生突变?

天真的实现:

var messages = [
  {insertedAt: "2021-01-10"},
  {insertedAt: "2021-01-12"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-14"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-16"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-18"},
  {insertedAt: "2021-01-18"},
]

var messagesGroupedByDate = messages.reduce(function (data, message) {
  if (
    data.some(function (point) {
      return point.date === message.insertedAt;
    })
  ) {
    return data.map(function (point) {
      if (point.date === message.insertedAt) {
        return {
          date: point.date,
          count: (point.count + 1) | 0,
        };
      } else {
        return point;
      }
    });
  } else {
    return data.concat([
      {
        date: message.insertedAt,
        count: 1,
      },
    ]);
  }
}, []);


console.log(messagesGroupedByDate);

为了争论,没有必要使它更通用。我面临的问题是我循环了三次:

  • 一次Array.prototype.reduce需要循环messages
  • 一次Array.prototype.some查看日期键是否已存在于结果数组中
  • 在日期键已经存在的情况下,我们再次循环Array.prototype.map以更新数组的特定元素
  • 否则,返回一个包含新元素的新数组

如果在 ReScript 中没有真正提高效率的好方法,那么我总是可以为这个函数使用原始 JavaScript,但我很好奇是否有可能在没有突变的情况下有效地做到这一点。

4

4 回答 4

1

您可以通过按日期构建计数对象来更简单地进行分组,然后根据需要使用Object.entriesandArray.map将其转换为对象数组:

var messages = [
  {insertedAt: "2021-01-10"},
  {insertedAt: "2021-01-12"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-14"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-16"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-18"},
  {insertedAt: "2021-01-18"},
];

var messagesGroupedByDate = Object.entries(
  messages.reduce((data, message) => {
    data[message.insertedAt] = data[message.insertedAt] || 0;
    data[message.insertedAt]++;
    return data;
  }, {})
).map(([date, count]) => ({ date, count }));

console.log(messagesGroupedByDate);

您还可以直接从中的值创建一个对象messages,然后通过循环更新计数messages

var messages = [
  {insertedAt: "2021-01-10"},
  {insertedAt: "2021-01-12"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-14"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-16"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-18"},
  {insertedAt: "2021-01-18"},
]

const data = Object.fromEntries(messages.map(({ insertedAt: date }) => [ date, 0 ]));

messages.forEach(({ insertedAt: date }) => data[date]++);

const messagesGroupedByDate = Object.entries(data).map(([date, count])=> ({date, count}));

console.log(messagesGroupedByDate);

于 2021-01-18T23:01:18.407 回答
1

只需将数据添加到 aMap()然后转换为数组,然后再转换为对象。它不会根据您的要求改变任何内容。

我们可以进一步简化这一点,但现在是凌晨 5:00,我的大脑现在睡着了。

var messages = [
  {insertedAt: "2021-01-10"},
  {insertedAt: "2021-01-12"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-14"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-16"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-18"},
  {insertedAt: "2021-01-18"},
];

const mapped = new Map();

messages.forEach(message => {
    // if date already seen before, increment the count
    if (mapped.has(message.insertedAt)) {
        const count = mapped.get(message.insertedAt);
        mapped.set(message.insertedAt, count+1);
    } else {
        // date never seen before, add to map with initial count
        mapped.set(message.insertedAt, 1);
    }
});

const msgArr = Array.from(mapped);

const final = msgArr.map(([date, count])=> ({date, count}));

console.log(final);

于 2021-01-18T23:19:03.167 回答
1
于 2021-01-18T23:25:17.110 回答
1

虽然这个问题有点老了,但我想我会为未来的读者分享我的代码。下面的代码是用编写的rescript并且完全不可变,因为我使用Immutable Map了 from rescript

type message = {insertedAt: string}

let messages = [
  {insertedAt: "2021-01-10"},
  {insertedAt: "2021-01-12"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-13"},
  {insertedAt: "2021-01-14"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-15"},
  {insertedAt: "2021-01-16"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-17"},
  {insertedAt: "2021-01-18"},
  {insertedAt: "2021-01-18"},
]

// Map Values
// Reduce into an immutable map
// Convert to tuple Array
// Log it

messages
->Belt.Array.reduce(Belt.Map.String.empty, (m, v) =>
  // Here Every set is creating a new map
  m->Belt.Map.String.set(v.insertedAt, m->Belt.Map.String.getWithDefault(v.insertedAt, 0) + 1)
)
->Belt.Map.String.toArray
->Js.log

在 Rescript Playground 中运行。更多关于 Immutable Map 的 rescript here

输出:

[ [ '2021-01-10', 1 ],
  [ '2021-01-12', 1 ],
  [ '2021-01-13', 3 ],
  [ '2021-01-14', 1 ],
  [ '2021-01-15', 2 ],
  [ '2021-01-16', 1 ],
  [ '2021-01-17', 3 ],
  [ '2021-01-18', 2 ] ]
于 2021-05-27T10:05:58.383 回答