【问题标题】:Javascript algorithm - section list of messages by dateJavascript算法 - 按日期列出的消息部分列表
【发布时间】:2021-01-21 22:20:45
【问题描述】:

简介

我正在从我的数据库中获取消息(使用双向分页(最近的消息在底部,旧消息在顶部),所以每次我获取 15 条消息时,我都会将它们分成各自的日期组。

我有点卡住了,因为如果你想获得良好的性能,算法有点复杂......

代码

这是我当前的代码:

export default class MessagesDatesSectioner {
  constructor(
    dateOptions = {
      weekday: "short",
      year: "numeric",
      month: "short",
      day: "numeric",
    }
  ) {
    this.dateOptions = dateOptions;
    this.sections = [];
    this.sectionsRef = new Map();
  }

  /**
  * Group a list of messages by date, generating a list of sections
  * which contains a title (the messages day as string), and an array
  * of data that contains all the messages.
  *
  * @param {list of objects}     messages     [{ date, text }, ...].
  */
  sectionizeMessagesByDate(messages) {
    let sections = messages.reduce((sections, message) => {
      const key =
        message.date
          .toDate?.()
          .toLocaleDateString(undefined, this.dateOptions) ||
        message.date.toLocaleDateString(undefined, this.dateOptions);

      if (!sections[key]) {
        sections[key] = [];
      }

      sections[key].push(message);

      return sections;
    }, {});

    sections = Object.keys(sections).map((key) => ({
      title: key,
      data: sections[key],
    }));

    // Merge the resulted sections with the existed ones
    this.mergeNewMessagesSections(sections);
  }

  /**
  * Get a list of new sections (or groups of messages), and merge them
  * with the existing sections.
  *
  * @param {list of objects}     newMessagesSections     [{ title: "Monday", data: [{date, text}, ...], }, ...].
  */
  mergeNewMessagesSections(newMessagesSections) {
    for (const { title, data } of newMessagesSections) {
      if (!this.sectionsRef.has(title)) {
        this.sectionsRef.set(title, this.sections.length);
        this.sections.push({ title, data: [] });
      }

      this.sections[this.sectionsRef.get(title)].data.push(...data);
    }
  }

  deleteMessage(message) {
    // TODO
  }
  
  // Return a copy of the sections list
  getSections() {
    return [...this.sections];
  }
}

问题

我的问题是在向这些结构中添加数据时出现的。由于我使用的是双向分页,因此新消息的日期可能比以前分段的消息更早或更晚。

例如:

  1- Call sectionizeMessagesByDate() with the following messages
          [
             { date: new Date("10/31/2000 00:00:01"), text: "Hello" },
             { date: new Date("10/31/2001 00:00:02"), text: "World" },
          ] // Note: All messages are sorted by date in the given array by default (Do not care about it)

  2- When merging, as there is no data in the sections list, it will generate the following:
          [
              {
                title: "10/31/2000",
                data: [ { date: new Date("10/31/2000 00:00:01"), text: "Hello" } ]  
              },
              {
                title: "10/31/2001", // Other year (different date)
                data: [ { date: new Date("10/31/2000 00:00:02"), text: "Hello" } ]  
              }
          ]  

如您所见,合并时新的部分会添加到当前部分列表的尾部...

因此,如果该类生成一个日期为“10/31/1999”的新部分,它将被添加到底部,而不是保留日期顺序。

任何想法如何实现节和节消息(在数据数组中)之间的排序(按日期)以保持良好的性能?

注意:map 'sectionsRef' 以节的标题为键,并在节列表中作为值进行索引,只是为了在将消息插入现有节时实现 O(1) 复杂度顺序。

【问题讨论】:

  • 您通常会排序多少个项目?
  • @jarmod 此算法用于聊天屏幕(取决于用户滚动)。在每次滚动到达(或到达顶部,因为双向)时,用户从数据库中获得 15 条新消息(按日期排序)。这15条直接传给这个类(messagesDatesSectioner.sectionizeMessagesByDate(listOfMessages))。

标签: javascript algorithm performance optimization ecmascript-6


【解决方案1】:

这是我能想到的最好的了

(未实现,我写了伪代码)

有很多假设,但我想你说的是典型的 WhatsApp 聊天。

为了对部分和消息进行排序,您还必须添加一个“firstMessageDate”字段(在伪称为“date”中),它必须是一个 JS 日期对象,因为您不能按如下方式构建它们:

  const date = new Date("Monday"); // Invalid date

这是我的意大利面条算法:

  mergeNewMessagesSections(newMessagesSections) {
    for (const { title, data } of newMessagesSections) {
      // If the section doesnt exist...
      if (!this.sectionsRef.has(title)) {
        // 1. Insert the section at the head or at the tail of the existing sections list?
        // if (section.date > sections[0].date)
        //       insert the older section at the head of the existing sections list
        //       Also, reorder (recalculate) the sectionsRef indices and add it to the head of the map
        // else
        //       insert the newer section at the tail of the existing sections list
        //       No map reordering required, just insert the section at the end of the refs map
        //
        //
        //
        // Note:
        //     In the typical chat screens, the top section of messages is always older than the middle section of messages
        //     In the other hand, the bottom section of messages is always newer then the middle ones.

        this.sections.push({ title, data: [] });
        this.sectionsRef.set(title, this.sections.length);
      }

      // 2. Insert the messages at the head or at the tail of the existing section?
      // if (section.date >= sections[this.sectionsRef.get(title)].data[0])
      //       insert the old messages at the head of the section
      // else
      //       insert the most fresh messages at the tail of the section
      //
      //
      //  Note: (as happen with sections)
      //      In chat screens, the top messages are always older than the middle messages.
      //      In the other hand, the bottom messages are always newer than the middle ones.
      //

      this.sections[this.sectionsRef.get(title)].data.push(...data);
    }
  }

【讨论】:

    猜你喜欢
    • 2022-06-29
    • 2013-09-07
    • 2019-04-02
    • 2010-09-09
    • 2018-10-07
    • 2012-08-26
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    相关资源
    最近更新 更多