【问题标题】:simplification of imbricated javascript for loops叠瓦式 javascript for 循环的简化
【发布时间】:2019-11-11 09:58:44
【问题描述】:

我有几个级别的深 javscript 对象。所有级别都是对象,除了最终级别是我需要排序的数组。

到目前为止,我的代码如下所示:

for (let group in objRes) {
    if (objRes.hasOwnProperty(group)) {
        for (let type in objRes[group]) {
            if (objRes[group].hasOwnProperty(type)) {
                for (let name in objRes[group][type]) {
                    if (objRes[group][type].hasOwnProperty(name)) {
                        for (let tenor in objRes[group][type][name]) {
                            if (objRes[group][type][name].hasOwnProperty(tenor)) {
                                objRes[group][type][name][tenor] = objRes[group][type][name][tenor].sort((x,y)=>x.date>y.date);
                            }
                        }
                    }
                }
            }
        }
    }
}

级别 (group,type,name,tenor) 都是字符串,最后一级数组成员如下所示:{date:'2019-12-25',value:35}

所以objRes 看起来像

{
group1:
    {type1:
        {name1:
            {tenor1:[{date:'2019-12-25',value:35},...],
         name2 :{...}
         }
    },
    {type2 :{...}},
group2:{...}
}

有没有巧妙的方法来简化这个?

您可以假设级别数已知或未知。

【问题讨论】:

  • 请用objRes的内容的代表性示例更新您的问题。
  • 如果级别数是任意的,什么条件会告诉您停止并执行sort?该级别的对象是什么?
  • 您可能可以编写一个递归函数来检查它是否具有较低的级别
  • 该更新没有提供objRes 内容的代表性示例,完全不清楚对象的末端在哪里(您的大括号不匹配)。如果您不愿意努力解决您的问题以确保其清晰、提供有用的样本数据等,那么人们为什么要努力回答它?

标签: javascript arrays sorting object recursion


【解决方案1】:

您可以为此使用递归函数。根据问题中的信息很难给出一个确切的例子,但例如:

function process(obj) {
    // Loop through the values of the own properties of the object
    for (const value of Object.values(obj)) {
        // Is this the termination condition?
        if (Array.isArray(value)) { // <== A guess at the condition, adjust as needed
            // We've reached the bottom
            value.sort((x, y) => x.date.localeCompare(y.date)); // <== Note correction, you can't just return the result of `>`
        } else {
            // Not the termination, recurse
            process(value);
        }
    }
}

使用猜测数据的实时示例:

function process(obj) {
    // Loop through the values of the own properties of the object
    for (const value of Object.values(obj)) {
        // Is this the termination condition?
        if (Array.isArray(value)) { // <== A guess at the condition, adjust as needed
            // We've reached the bottom
            value.sort((x, y) => x.date.localeCompare(y.date)); // <== Note correction, you can't just return the result of `>`
        } else {
            // Not the termination, recurse
            process(value);
        }
    }
}

const objRes = {
    group1: {
        type1: {
            name1: {
                tenor1: [
                    {date: '2019-12-23', value: 35},
                    {date: '2019-12-25', value: 32},
                    {date: '2019-12-24', value: 30},
                ]
            },
            name2 :[]
        },
        type2: {}
    },
    group2: {}
};
process(objRes);
console.log(JSON.stringify(objRes, null, 4));
.as-console-wrapper {
    max-height: 100% !important;
}

一些注意事项:

  1. 您可以通过使用Object.values 来避免for-in/hasOwnProperty 组合。

  2. 您可以使用for-of 遍历这些值。

  3. 必须有 some 终止条件告诉函数何时到达“底部”。在该示例中,我使用了Array.isArray,因为您在最后一级进行排序。

  4. Array.prototype.sort直接修改数组,不需要使用它的返回值。

  5. 传递给sort 的函数必须返回负数、0 或正数,而不是布尔值(更多here)。由于您的date 值似乎是yyyy-MM-dd 形式的字符串,因此您可以使用localeCompare 来执行此操作(因为在这种格式下,字典比较也是日期比较)。

    李>

【讨论】:

    【解决方案2】:

    这可以替代 T.J. 的回答。 Crowder 采用了其中提到的几个决策点,并对其进行了显式的函数调用。

    const process = (test, transform, data) =>
      typeof data == "object"
        ? ( Array .isArray (data)
           ? (xs) => xs .map (([_, x]) => x)
           : Object .fromEntries
          ) ( Object .entries (data) .map (([k, v]) => 
            [k, test (k, v) ? transform (v) : process (test, transform, v)]
          ))
        : data  
    

    它接受两个函数以及您的数据对象。第一个是测试您是否达到了要按摩的嵌套值。所以我们可以想象像(k, v) =&gt; k .startsWith ('tenor')(k, v) =&gt; Array .isArray (v) 这样的东西。第二个函数接受该条目的值并返回一个更新的值,可能是(v) =&gt; v . sort((a, b) =&gt; a .date .localeCompare (b .date))

    (注意:您的排序调用有问题。不要使用.sort ((a, b) =&gt; a &lt; b),它返回一个布尔值,然后强制转换为01,而适当的比较器当a &lt; b 时应该返回-1。如果您可以与&lt; 进行比较,那么这应该始终有效(a, b) =&gt; a &lt; b ? -1 : a &gt; b ? 1 : 0。我不知道是否有比Sorting an array with .sort((a,b) => a>b) works. Why? 更完整的SO 问题。)

    你可以在这个 sn-p 中看到它的实际效果:

    const process = (test, transform, data) =>
      typeof data == "object"
        ? ( Array .isArray (data)
            ? (xs) => xs .map (([_, x]) => x)
            : Object .fromEntries
          ) ( Object .entries (data) .map (([k, v]) => 
            [k, test (k, v) ? transform (v) : process (test, transform, v)]
          ))
        : data    
    
    const dateSort = (xs) => xs .slice (0) .sort ((a, b) => a .date .localeCompare (b .date))
    
    const objRes = {
        group1: {
            type1: {
                name1: {
                    tenor1: [
                        {date: '2019-12-23', value: 35},
                        {date: '2019-12-25', value: 32},
                        {date: '2019-12-24', value: 30},
                    ]
                },
                name2: []
            },
            type2: {}
        },
        group2: {
            type3: [
                {date: '2020-01-03', value: 42},
                {date: '2019-01-01', value: 43},
                {date: '2019-01-02', value: 44},
            ]
        }
    };
    
    // This one only sorts the first group of dates
    console .log (
      process (
        (k, v) => k .startsWith ('tenor'),
        dateSort,
        objRes
      )
    )
    
    // This one sorts both groups
    console .log (
      process (
        (k, v) => Array .isArray (v),
        dateSort,
        objRes
      )
    )

    代码有点密集。最外层的条件只有在数据是对象时才应用我们的处理,如果不是,则原封不动地返回,如果是,则进行以下处理:

    我们首先使用Object.entries 将我们的对象转换为名称-值对,然后通过测试每一对来映射该对象以查看我们是否遇到了我们应该转换的内容并返回转换后的值或递归的结果关于价值。

    也许最棘手的一点是:

          ( Array .isArray (data)
            ? (xs) => xs .map (([_, x]) => x)
            : Object .fromEntries
          )
    

    在这里,我们只需选择一个函数来应用到转换后的对以将其重新变成一个整体。如果它是一个数组,我们跳过键并返回一个值数组。如果不是,我们使用Object.fromEntries 来创建它们的对象。如果Object.entriesnot available in your environment,则很容易填充。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-11
      • 2016-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多