【问题标题】:Sorting Javascript entries by a value按值对 Javascript 条目进行排序
【发布时间】:2021-07-22 00:47:54
【问题描述】:

我在 firebase 实时数据库中有一个 javascript 对象,其结构类似于以下内容:

const obj = {
  "quotes" : {
    "Anon" : {
      "-MZN5R9TUU5eLneHz2F_" : {
        "quote" : "I am a quote",
        "timestamp" : "1619616711347",
        "htmlId" : "id517321"
      }
    },
    "Secret Quoter" : {
      "-D9NF75TGVSeJLABz1W_" : {
        "quote" : "I am another quote",
        "timestamp" : "1690016711317",
        "htmlId" : "id519912"
      },
      "-DZNfR5THESeJLeHz1F_" : {
        "quote" : "I am a different quote",
        "timestamp" : "1349616899347",
        "htmlId" : "id515618"
      }
    },
    "General Kenobi" : {
      "-MZR666TUUGeLneHzHF_" : {
        "quote" : "Hello There",
        "timestamp" : "1919616711347",
        "htmlId" : "id511321"
      }
    }
  }
};

现在我想根据每个对象的最高 timestamp 属性重新排序对象条目(假设每个引用作者的嵌入对象都按时间戳正确排序),因此在这种情况下解析的响应将是:

{
  "quotes" : {
    "General Kenobi" : {
      "-MZR666TUUGeLneHzHF_" : {
        "quote" : "Hello There",
        "timestamp" : "1919616711347",
        "htmlId" : "id511321"
      }
    },
    "Secret Quoter" : {
      "-D9NF75TGVSeJLABz1W_" : {
        "quote" : "I am another quote",
        "timestamp" : "1690016711317",
        "htmlId" : "id519912"
      },
      "-DZNfR5THESeJLeHz1F_" : {
        "quote" : "I am a different quote",
        "timestamp" : "1349616899347",
        "htmlId" : "id515618"
      }
    },
    "Anon" : {
      "-MZN5R9TUU5eLneHz2F_" : {
        "quote" : "I am a quote",
        "timestamp" : "1619616711347",
        "htmlId" : "id517321"
      }
    }
  }
};

我该怎么做呢?似乎有很多关于对对象数组进行排序的建议,但对于根据单个对象的实体的嵌入值实际排序的建议却没有。


编辑:感谢所有答案。总而言之,我通过重新设计我的 DB 模式完全避免了这个问题,以便 quotes 是一个对象数组,每个对象都有一个 author 属性。例如

{
  "quotes" : [
     {
        "author" : "General Kenobi",
        "quote" : "Hello There",
        "timestamp" : "1919616711347",
        "htmlId" : "id511321"
     },
     // More quotes here...
  ]

然后可以使用sort() 的传统方法将列表按最高时间戳向下排序。为了回答问题,我仍然会接受其中一个建议,但我建议读者在尝试订购对象之前考虑其对象的结构是否正确,因为使用数组更容易做到这一点.

【问题讨论】:

  • 出于好奇(也许会导致更有意义的答案)...为什么在这种情况下对象属性的顺序很重要?该排序在哪里使用?
  • 需要注意的一点是,根据 JS 引擎和版本,对象属性的顺序可能不一致或无法保证。在 JS 中,如果需要为集合中的项目指定顺序,通常会使用数组或其他数据结构
  • @David 顺序很重要,因为它们是如何被迭代和显示的。角度 ngFor 组件的不同部分需要读取此引用对象的不同部分(firebase db 显然比我在这里展示的要多)。为了首先显示最新的报价,我需要在表格中首先显示报价作者,然后是第二个作者及其报价,依此类推
  • 您能否描述一下排序后您将如何遍历引号?我明白你想做什么,但不明白为什么
  • 我建议您查看此 Q&A stackoverflow.com/questions/5525795/… 它解释了为什么您描述的任务不是最好的主意

标签: arrays typescript sorting object firebase-realtime-database


【解决方案1】:

正如其他人所说,对象中的插入顺序没有得到可靠维护。相反,我们可以为最终对象使用数组或映射。为了排序,我们需要将您的对象重新格式化为嵌套数组的数组。如果您想使用地图作为最终对象,我们也可以这样做。我们只需要首先使用一些数组和对象方法sort()Object.entries()map() 转换我们的排序数组,如下所示:

const obj={quotes:{Anon:{"-MZN5R9TUU5eLneHz2F_":{quote:"I am a quote",timestamp:"1619616711347",htmlId:"id517321"}},"Secret Quoter":{"-D9NF75TGVSeJLABz1W_":{quote:"I am another quote",timestamp:"1690016711317",htmlId:"id519912"},"-DZNfR5THESeJLeHz1F_":{quote:"I am a different quote",timestamp:"1349616899347",htmlId:"id515618"}},"General Kenobi":{"-MZR666TUUGeLneHzHF_":{quote:"Hello There",timestamp:"1919616711347",htmlId:"id511321"}}}};

const newObj = { quotes: Object.entries(obj.quotes).map(e => [e[0], Object.entries(e[1]).map(f => f.map((g,k,c) => k ? { id: c[k-1], ...g } : g)[1]).sort((a,b) => parseInt(b.timestamp) - parseInt(a.timestamp))]).sort((a,b) => parseInt(b[1][0].timestamp) - parseInt(a[1][0].timestamp)) };

console.log(newObj);

由于对象的深度,我们不得不执行上面的几个嵌套映射方法。但是,这会产生所需的顺序并将可靠地保留该顺序。现在剩下的就是将我们的对象转换为一个数组,如果您希望它具有关联功能:

const obj={quotes:{Anon:{"-MZN5R9TUU5eLneHz2F_":{quote:"I am a quote",timestamp:"1619616711347",htmlId:"id517321"}},"Secret Quoter":{"-D9NF75TGVSeJLABz1W_":{quote:"I am another quote",timestamp:"1690016711317",htmlId:"id519912"},"-DZNfR5THESeJLeHz1F_":{quote:"I am a different quote",timestamp:"1349616899347",htmlId:"id515618"}},"General Kenobi":{"-MZR666TUUGeLneHzHF_":{quote:"Hello There",timestamp:"1919616711347",htmlId:"id511321"}}}};

// This is a helper function so we can console.log the Map type in the StackOverflow console. This is not needed for actual production use though.
const maplog = (_,v) => v instanceof Map ? Array.from(v.entries()) : v;

const newObj = { quotes: Object.entries(obj.quotes).map(e => [e[0], Object.entries(e[1]).map(f => f.map((g,k,c) => k ? { id: c[k-1], ...g } : g)[1]).sort((a,b) => parseInt(b.timestamp) - parseInt(a.timestamp)).reduce((a,{id,quote,timestamp,htmlId}) => (a.set(id, { quote, timestamp, htmlId })), new Map())]).sort((a,b) => parseInt(b[1].values().next().value.timestamp) - parseInt(a[1].values().next().value.timestamp)).reduce((a,c) => (a.set(c[0], c[1])), new Map()) };

console.log(JSON.stringify(newObj, maplog, 2));

【讨论】:

    【解决方案2】:

    对象不保证排序顺序。

    1. 先尝试将数据集缩减为平面列表 (flattened)
    2. 构建“quoter”到“timestamp”的查找映射 (maxTimeMap)
    3. 按查找排序,后跟时间戳降序排列 (b - a)。

    const main = () => {
      const flattened = Object.entries(obj.quotes)
        .reduce((accOuter, [quoter, quotes]) =>
          Object.entries(quotes)
            .reduce((accInner, [id, { quote, timestamp, htmlId }]) =>
              [...accInner, { id, quoter, quote, timestamp, htmlId }], accOuter), [])
    
      const maxTimeMap = flattened.reduce((acc, { quoter, timestamp }) =>
        ({ ...acc, [quoter]: Math.max(acc[quoter] || Number.MIN_VALUE, timestamp) }), {});
    
      const sorted = flattened.sort((
        { timestamp: ta, quote: qa, quoter: qra },
        { timestamp: tb, quote: qb, quoter: qrb }
      ) => maxTimeMap[qrb] - maxTimeMap[qra] || tb - ta);
    
      console.log(sorted);
    };
    
    const obj = {
      "quotes": {
        "Anon": {
          "-MZN5R9TUU5eLneHz2F_": {
            "quote": "I am a quote",
            "timestamp": "1619616711347",
            "htmlId": "id517321"
          }
        },
        "Secret Quoter": {
          "-D9NF75TGVSeJLABz1W_": {
            "quote": "I am another quote",
            "timestamp": "1690016711317",
            "htmlId": "id519912"
          },
          "-DZNfR5THESeJLeHz1F_": {
            "quote": "I am a different quote",
            "timestamp": "1349616899347",
            "htmlId": "id515618"
          }
        },
        "General Kenobi": {
          "-MZR666TUUGeLneHzHF_": {
            "quote": "Hello There",
            "timestamp": "1919616711347",
            "htmlId": "id511321"
          }
        },
      }
    };
    
    main();
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    【讨论】:

      【解决方案3】:

      最简单的方法是使用像 lodash 这样的库来为您执行此操作。

      _.orderBy(obj.quotes, el => Object.values(el)[0].timestamp, ['desc'])

      但您也可以使用数组操作来获得所需的结果。

      1. 将对象转换为数组
      2. 按所需顺序对数组进行排序
      3. 重新构造对象
      const tempArr = Object.entries(obj.quotes)
      
      // sorts into ascending order based on a property
      tempArr.sort(
          (a, b) => Object.values(a[1])[0].timestamp - Object.values(b[1])[0].timestamp
      )
      
      // since we want the descending order, reverse the array and construct the object back
      const result = { quotes: Object.fromEntries(tempArr.reverse()) }
      console.log(result)
      

      【讨论】:

      • 按第一个时间戳排序,而不是最高时间戳。
      • 假设作者时间戳已经按最高排序(在本例中为最高),那么这是正确的。但不是正确的解决方案,否则@3limin4t0r 说
      猜你喜欢
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      • 2020-04-11
      • 2023-02-05
      • 1970-01-01
      • 2018-11-01
      • 2016-07-09
      相关资源
      最近更新 更多