簡體   English   中英

有效地重命名/重新映射對象數組中的 javascript/json 對象鍵

[英]Efficiently rename/re-map javascript/json object keys within array of objects

我有一些像這樣的結構化 JSON 數據。 讓我們假設這是可互換的,通過JSON.parse()

[
    {
        "title": "pineapple",
        "uid": "ab982d34c98f"
    },
    {
        "title": "carrots",
        "uid": "6f12e6ba45ec"
    }
]

我需要它看起來像這樣,將title重新映射到name ,將uid重新映射到id並得到結果:

[
    {
        "name": "pineapple",
        "id": "ab982d34c98f"
    },
    {
        "name": "carrots",
        "id": "6f12e6ba45ec"
    }
]

最明顯的做法是這樣的:

str = '[{"title": "pineapple","uid": "ab982d34c98f"},{"title": "carrots", "uid": "6f12e6ba45ec"}]';

var arr = JSON.parse(str);
for (var i = 0; i<arr.length; i++) {
    arr[i].name = arr[i].title;
    arr[i].id = arr[i].uid;
    delete arr[i].title;
    delete arr[i].uid;
}

 str = '[{"title": "pineapple","uid": "ab982d34c98f"},{"title": "carrots", "uid": "6f12e6ba45ec"}]'; var arr = JSON.parse(str); for (var i = 0; i<arr.length; i++) { arr[i].name = arr[i].title; arr[i].id = arr[i].uid; delete arr[i].title; delete arr[i].uid; } $('body').append("<pre>"+JSON.stringify(arr, undefined, 4)+"</pre>");
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

...或使用更復雜的東西(雖然不是更有效),像這樣

這一切都很好,但如果數組中有 200,000 個對象呢? 這是大量的處理開銷。

有沒有更有效的方法來重新映射鍵名? 可能不循環遍歷整個對象數組? 如果您的方法更有效,請提供證明/參考。

正如我在評論中已經提到的,如果您可以對對象的值做出某些假設,則可以使用正則表達式來替換鍵,例如:

str = str.replace(/"title":/g, '"name":');

它不是那么“干凈”,但它可能會更快地完成工作。


如果您無論如何都必須解析 JSON,則更結構化的方法是reviver函數傳遞給JSON.parse ,您可能能夠避免對數組進行額外的傳遞。 這可能取決於引擎如何實現JSON.parse (也許他們首先解析整個字符串,然后使用 reviver 函數進行第二次傳遞,在這種情況下你不會得到任何好處)。

var arr = JSON.parse(str, function(prop, value) {
   switch(prop) {
     case "title":
        this.name = value;
        return;
     case "uid":
        this.id = value;
        return;
     default:
        return value;
   }
});

基准測試,使用下面的 Node.js 腳本進行了 3 次測試:

1389822740739: Beginning regex rename test
1389822740761: Regex rename complete
// 22ms, 22ms, 21ms
1389822740762: Beginning parse and remap in for loop test
1389822740831: For loop remap complete
// 69ms, 68ms, 68ms
1389822740831: Beginning reviver function test
1389822740893: Reviver function complete
// 62ms, 61ms, 60ms

看起來好像正則表達式(在這種情況下)是最有效的,但是在嘗試使用正則表達式解析 JSON 時要小心


測試腳本,加載 100,230 行 OP 的示例 JSON:

fs = require('fs');
fs.readFile('test.json', 'utf8', function (err, data) {
    if (err) {
        return console.log(err);
    }
    console.log(new Date().getTime() + ": Beginning regex rename test");
    var str = data.replace(/"title":/g, '"name":');
    str = str.replace(/"uid":/g, '"id":');
    JSON.parse(str);
    console.log(new Date().getTime() + ": Regex rename complete");
    console.log(new Date().getTime() + ": Beginning parse and remap in for loop test");
    var arr = JSON.parse(data);
    for (var i = 0; i < arr.length; i++) {
        arr[i].name = arr[i].title;
        arr[i].id = arr[i].uid;
        delete arr[i].title;
        delete arr[i].uid;
    }
    console.log(new Date().getTime() + ": For loop remap complete");
    console.log(new Date().getTime() + ": Beginning reviver function test");
    var arr = JSON.parse(data, function (prop, value) {
        switch (prop) {
            case "title":
                this.name = value;
                return;
            case "uid":
                this.id = value;
                return;
            default:
                return value;
        }
    });
    console.log(new Date().getTime() + ": Reviver function complete");
});

很久以前問過這個問題,從那時起,我已經習慣於使用Array.prototype.map()來完成工作,更多的是為了代碼的穩定性和清潔度而不是性能。 雖然它肯定不是性能最好的,但它看起來很棒:

var repl = orig.map(function(obj) {
    return {
        name: obj.title,
        id: obj.uid
    }
})

如果您需要更靈活(和 ES6 兼容的功能),請嘗試:

let replaceKeyInObjectArray = (a, r) => a.map(o => 
    Object.keys(o).map((key) => ({ [r[key] || key] : o[key] })
).reduce((a, b) => Object.assign({}, a, b)))

例如

const arr = [{ abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }]
const replaceMap = { "abc": "yyj" }

replaceKeyInObjectArray(arr, replaceMap)

/*
[
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    },
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    },
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    }
]
*/

這是 OP 建議使用map()為清晰起見(不是性能)的另一種看法。

var newItems = items.map(item => ({
    name: item.title,
    id: item.uid
}));

這使用ES6 箭頭函數和快捷語法,當只有一個參數傳遞給函數並且函數體中只有一個語句時,這些語法是可能的。

根據您使用各種語言的 lambda 表達式的歷史,這種形式可能會也可能不會引起您的共鳴。

在像這樣的箭頭函數快捷語法中返回對象文字時要小心。 不要忘記對象文字周圍的附加括號!

如果你想讓它更可重用一點。 也許這是一個體面的方法。

 function rekey(arr, lookup) { for (var i = 0; i < arr.length; i++) { var obj = arr[i]; for (var fromKey in lookup) { var toKey = lookup[fromKey]; var value = obj[fromKey]; if (value) { obj[toKey] = value; delete obj[fromKey]; } } } return arr; } var arr = [{ apple: 'bar' }, { apple: 'foo' }]; var converted = rekey(arr, { apple: 'kung' }); console.log(converted);

使用 ES6:

const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
  return arr.map(s => {
    return Object.keys(s).reduce((prev, next) => {
      if(next === oldField) { 
        prev[newField] = s[next]
      } else { 
        prev[next] = s[next] 
      }
      return prev
    }, {})
  })
}

使用 ES7:

const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
  return arr.map(s => {
    return Object.keys(s).reduce((prev, next) => {
      return next === oldField
        ? {...prev, [newField]: s[next]}
        : {...prev, [next]: s[next]}
    }, {})
  })
}
var jsonObj = [/*sample array in question*/   ]

基於下面討論的不同基准,最快的解決方案是原生的:

var arr = [];
for(var i = 0, len = jsonObj .length; i < len; i++) {
  arr.push( {"name": jsonObj[i].title, "id" : jsonObj[i].uid});
}

我認為或者不使用框架,這將是選項 2:

var arr = []
jsonObj.forEach(function(item) { arr.push({"name": item.title, "id" : item.uid }); });

在使用 Navite 和非 Navite 函數之間總是存在爭論。 如果我沒記錯的話, lodash認為它們比下划線更快,因為關鍵操作使用非本機函數。

然而,不同的瀏覽器有時會產生非常不同的結果。 我一直在尋找最好的平均值。

對於基准測試,您可以查看以下內容:

http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8

您可以使用名為node-data-transform的 npm 包。

您的數據:

const data = [
  {
    title: 'pineapple',
    uid: 'ab982d34c98f',
  },
  {
    title: 'carrots',
    uid: '6f12e6ba45ec',
  },
];

你的映射:

const map = {
  item: {
    name: 'title',
    id: 'uid',
  },
};

並使用包:

const DataTransform = require("node-json-transform").DataTransform;
const dataTransform = DataTransform(data, map);
const result = dataTransform.transform();
console.log(result);

結果 :

[
  {
    name: 'pineapple',
    id: 'ab982d34c98f'
  },
  {
    name: 'carrots',
    id: '6f12e6ba45ec'
  }
]

也許這不是性能的最佳方式,但它非常優雅。

function replaceElem(value, replace, str) {
            while (str.indexOf(value) > -1) {
                str = str.replace(value, replace);
            }
            return str;
        }

從主調用這個

var value = "tittle";
var replace = "name";
replaceElem(value, replace, str);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM