【问题标题】:Renaming object keys recursively递归重命名对象键
【发布时间】:2026-02-11 20:30:02
【问题描述】:

我有一个递归函数来重命名对象的键名,但我无法弄清楚如何重命名其中的 2 个键(问题键是对象)

我认为问题出在我正在检查对象类型的位置,但我如何在那时重命名键?

实际的数组非常大,但下面是缩减版。

任何帮助表示赞赏。

var keys_short = ['ch','d','u','tz'];
var keys_long = ['children','data','user_id','time_zone'];
function refit_keys(o){
    build = {};
    for (var i in o){
        if(typeof(o[i])=="object"){
            o[i] = refit_keys(o[i]);
            build = o;
        }else{
            var ix = keys_short.indexOf(i);
            if(ix!=-1){
                build[keys_long[ix]] = o[keys_short[ix]];
            }
        }
    }
    return build;
}

我的输入如下所示:

{
    "id":"1",
    "ch":[
        {
            "id":"3",
            "ch":[
            ],
            "d":{
                "u":"3",
                "tz":"8.00"
            }
        },
        {
            "id":"45",
            "ch":[
                {
                    "id":"70",
                    "ch":[
                        {
                            "id":"43",
                            "ch":[
                            ],
                            "d":{
                                "u":"43",
                                "tz":"-7.00"
                            }
                        }
                    ],
                    "d":{
                        "u":"70",
                        "tz":"-7.00"
                    }
                }
            ],
            "d":{
                "u":"45",
                "tz":"-7.00"
            }
        }
    ],
    "d":{
        "u":"1",
        "tz":"8.00"
    }
}

我的输出是这样的:

{
    "id":"1",
    "ch":[
        {
            "id":"3",
            "ch":[
            ],
            "d":{
                "user_id":"3",
                "time_zone":"8.00"
            }
        },
        {
            "id":"45",
            "ch":[
                {
                    "id":"70",
                    "ch":[
                        {
                            "id":"43",
                            "ch":[
                            ],
                            "d":{
                                "user_id":"43",
                                "time_zone":"-7.00"
                            }
                        }
                    ],
                    "d":{
                        "user_id":"70",
                        "time_zone":"-7.00"
                    }
                }
            ],
            "d":{
                "user_id":"45",
                "time_zone":"-7.00"
            }
        }
    ],
    "d":{
        "user_id":"1",
        "time_zone":"8.00"
    }
}

【问题讨论】:

    标签: javascript jquery recursion


    【解决方案1】:

    这里有几个问题。

    其中一个原因是您未能在函数中声明您的build 变量,从而成为The Horror of Implicit Globals 的牺牲品。

    但是逻辑也有问题,这里有一个最小的修改:

    var keys_short = ["ch","d","u","tz"];
    var keys_long = ["children","data","user_id","time_zone"];
    function refit_keys(o){
        var build, key, destKey, ix, value;
    
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        // We have a non-array object
        build = {};
        for (key in o) {
            // Get the destination key
            ix = keys_short.indexOf(key);
            destKey = ix === -1 ? key : keys_long[ix];
    
            // Get the value
            value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    

    现场示例:

    "use strict";
    var input = {
        "id":"1",
        "ch":[
            {
                "id":"3",
                "ch":[
                ],
                "d":{
                    "u":"3",
                    "tz":"8.00"
                }
            },
            {
                "id":"45",
                "ch":[
                    {
                        "id":"70",
                        "ch":[
                            {
                                "id":"43",
                                "ch":[
                                ],
                                "d":{
                                    "u":"43",
                                    "tz":"-7.00"
                                }
                            }
                        ],
                        "d":{
                            "u":"70",
                            "tz":"-7.00"
                        }
                    }
                ],
                "d":{
                    "u":"45",
                    "tz":"-7.00"
                }
            }
        ],
        "d":{
            "u":"1",
            "tz":"8.00"
        }
    };
    
    var keys_short = ["ch","d","u","tz"];
    var keys_long = ["children","data","user_id","time_zone"];
    function refit_keys(o){
        var build, key, destKey, ix, value;
    
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        // We have a non-array object
        build = {};
        for (key in o) {
            // Get the destination key
            ix = keys_short.indexOf(key);
            destKey = ix === -1 ? key : keys_long[ix];
    
            // Get the value
            value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    
    console.log(refit_keys(input));
    .as-console-wrapper {
        max-height: 100% !important;
    }

    但我建议使用映射,而不是并行数组,通过对象或Map

    // Object with no prototype to avoid false matches on `toString` and other built-ins
    var mapShortToLong = Object.assign(Object.create(null), {
        "ch": "children",
        "d":  "data",
        "u":  "user_id",
        "tz": "time_zone"
    });
    function refit_keys(o){
        var build, key, destKey, value;
    
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        build = {};
        for (key in o) {
            // Get the destination key
            destKey = mapShortToLong[key] || key;
    
            // Get the value
            value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    

    现场示例:

    "use strict";
    var input = {
        "id":"1",
        "ch":[
            {
                "id":"3",
                "ch":[
                ],
                "d":{
                    "u":"3",
                    "tz":"8.00"
                }
            },
            {
                "id":"45",
                "ch":[
                    {
                        "id":"70",
                        "ch":[
                            {
                                "id":"43",
                                "ch":[
                                ],
                                "d":{
                                    "u":"43",
                                    "tz":"-7.00"
                                }
                            }
                        ],
                        "d":{
                            "u":"70",
                            "tz":"-7.00"
                        }
                    }
                ],
                "d":{
                    "u":"45",
                    "tz":"-7.00"
                }
            }
        ],
        "d":{
            "u":"1",
            "tz":"8.00"
        }
    };
    
    // Object with no prototype to avoid false matches on `toString` and other built-ins
    var mapShortToLong = Object.assign(Object.create(null), {
        "ch": "children",
        "d":  "data",
        "u":  "user_id",
        "tz": "time_zone"
    });
    function refit_keys(o){
        var build, key, destKey, value;
    
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        build = {};
        for (key in o) {
            // Get the destination key
            destKey = mapShortToLong[key] || key;
    
            // Get the value
            value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    
    console.log(refit_keys(input));
    .as-console-wrapper {
        max-height: 100% !important;
    }

    或者在现代 JavaScript 中:

    // Using a `Map` here to provide a `Map` example, but you can ue an object as
    // in the previous ones if you prefer if the keys are strings
    const mapShortToLong = new Map([
        ["ch", "children"],
        ["d",  "data"],
        ["u",  "user_id"],
        ["tz", "time_zone"],
    ]);
    function refit_keys(o){
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        const build = {};
        for (const key in o) {
            // Get the destination key
            const destKey = mapShortToLong.get(key) || key;
    
            // Get the value
            let value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    

    现场示例:

    "use strict";
    var input = {
        "id":"1",
        "ch":[
            {
                "id":"3",
                "ch":[
                ],
                "d":{
                    "u":"3",
                    "tz":"8.00"
                }
            },
            {
                "id":"45",
                "ch":[
                    {
                        "id":"70",
                        "ch":[
                            {
                                "id":"43",
                                "ch":[
                                ],
                                "d":{
                                    "u":"43",
                                    "tz":"-7.00"
                                }
                            }
                        ],
                        "d":{
                            "u":"70",
                            "tz":"-7.00"
                        }
                    }
                ],
                "d":{
                    "u":"45",
                    "tz":"-7.00"
                }
            }
        ],
        "d":{
            "u":"1",
            "tz":"8.00"
        }
    };
    
    // Using a `Map` here to provide a `Map` example, but you can ue an object as
    // in the previous ones if you prefer if the keys are strings
    const mapShortToLong = new Map([
        ["ch", "children"],
        ["d",  "data"],
        ["u",  "user_id"],
        ["tz", "time_zone"],
    ]);
    function refit_keys(o){
        // Only handle non-null objects
        if (o === null || typeof o !== "object") {
            return o;
        }
    
        // Handle array just by handling their contents
        if (Array.isArray(o)) {
            return o.map(refit_keys);
        }
    
        const build = {};
        for (const key in o) {
            // Get the destination key
            const destKey = mapShortToLong.get(key) || key;
    
            // Get the value
            let value = o[key];
    
            // If this is an object, recurse
            if (typeof value === "object") {
                value = refit_keys(value);
            }
    
            // Set it on the result using the destination key
            build[destKey] = value;
        }
        return build;
    }
    
    console.log(refit_keys(input));
    .as-console-wrapper {
        max-height: 100% !important;
    }

    【讨论】:

    • 超越。效果很好,教会了我一些东西并找到了更好的方法
    • 有任何证据表明 seconf 方法更好/更快?
    • @benoror:我不认为我声称它更好或更快,但并行阵列通常是一个维护问题。太容易修改一个而不是另一个。错误地修改 half 的属性初始化器要困难得多。 ;-)
    • @PanMan - 谢谢!我已经解决了这个问题,并且通常会更新一些。
    【解决方案2】:

    有点晚了,但我正在寻找一个不错的简短实现,它也处理数组(之前的答案没有),所以我决定发布我的通用 ES6 实现,因为它可能帮助一些人:

    function deepMapKeys(originalObject, callback) {
      if (typeof originalObject !== 'object') {
        return originalObject
      }
    
      return Object.keys(originalObject || {}).reduce((newObject, key) => {
        const newKey = callback(key)
        const originalValue = originalObject[key]
        let newValue = originalValue
        if (Array.isArray(originalValue)) {
          newValue = originalValue.map(item => deepMapKeys(item, callback))
        } else if (typeof originalValue === 'object') {
          newValue = deepMapKeys(originalValue, callback)
        }
        return {
          ...newObject,
          [newKey]: newValue,
        }
      }, {})
    }
    

    对于有问题的情况,调用将是:

    deepMapKeys(inputObject, key => (keys_long[keys_short.indexOf(key)] || key))
    

    话虽如此,如果你可以使用 npm,那里有几个包(这里是oneanother..)

    【讨论】:

    • 感谢包的链接。
    【解决方案3】:

    其中一个问题可能是变量build 实际上是一个全局变量。因此,它只包含递归中第一个深度的结果。

    build 之前添加var 应该可以解决部分问题。

    【讨论】:

    • 不,这不是唯一的问题。这一个问题,它不是唯一的。
    【解决方案4】:

    对象键的递归映射函数更简洁的实现:

    const objectRecursiveKeyMap = (obj, fn) =>
      Object.fromEntries(Object.entries(obj).map(([key, value]) => {
        const getValue = v =>
          (typeof v === 'object' && v !== null) ? objectRecursiveKeyMap(v, fn) : v
    
        return [fn(key), Array.isArray(value)
          ? value.map(val => getValue(val))
          : getValue(value)]
      }))
    

    使用示例:

    objectRecursiveKeyMap(obj, key => key.toUpperCase())
    

    【讨论】:

      【解决方案5】:

      我喜欢这种方法。但我偶然发现了这个:

      你这样称呼它:deepMapKeys({testValue:["justString"]}, key => (key));

      returnValue 包含:

      {0: "j", 1: "u", 2: "s", 3: "t", 4: "S", 5: "t", 6: "r", 7: "i", 8: "n", 9: "g"}
      

      长度:1

      会说,它将我的字符串转换为数组。

      【讨论】:

      • 感谢@Chris H. 的反馈,我已经更新了解决用例的答案。你应该直接评论我的答案,因为这不是问题的答案,所以很混乱。另外,除非您对答案发表评论,否则我不会收到任何通知(我偶然注意到您的反馈)。干杯