web-dev-qa-db-ja.com

オブジェクトの配列内のjavascript / jsonオブジェクトキーの効率的な名前変更/再マッピング

そのような構造化されたJSONデータがいくつかあります。これが JSON.parse() を介して交換可能であると仮定しましょう:

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

titlenameに再マッピングし、uididに再マッピングして、次のようにする必要があります。

[
    {
        "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>

...または this のような、より複雑なもの(notより効率的)を使用します。

これはすべてうまくいきますが、配列に200,000個のオブジェクトがある場合はどうでしょうか。これは多くの処理オーバーヘッドです。

キー名を再マップするより効率的な方法はありますか?おそらくオブジェクトの配列全体をループせずに?メソッドがより効率的な場合は、証明/参照を提供してください。

21
brandonscript

コメントですでに述べたように、オブジェクトの値について特定の仮定を立てることができる場合、キーを置き換えるために正規表現を使用できます。次に例を示します。

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

それは「きれい」ではありませんが、仕事を早く終わらせるかもしれません。


とにかくJSONを解析する必要がある場合、より構造化されたアプローチは reviver関数をJSON.parse に渡すことです= =そして、配列への追加のパスを避けることができるかもしれません。これはおそらく、エンジンがJSON.parseを実装する方法に依存します(おそらく、文字列全体を最初に解析してから、リバイバー関数で2回目のパスを作成します。この場合、利点は得られません)。

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を解析しようとするときは注意してください


OPのサンプルJSONの100,230行をロードするテストスクリプト:

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");
});
25
Felix Kling

ずっと前にこの質問をしました。それ以来、パフォーマンスよりもコードの安定性とクリーンさのために、 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
    }
]
*/
12
brandonscript

わかりやすくするために(パフォーマンスではなく)map()を使用するというOPの提案の別の見解を次に示します。

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

これは ES6矢印関数 と、関数に渡されるパラメーターが1つのみで、関数の本体にステートメントが1つしかない場合に可能なショートカット構文を使用します。

さまざまな言語のラムダ式の履歴に応じて、このフォームはあなたに共鳴する場合としない場合があります。

このような矢印関数のショートカット構文でオブジェクトリテラルを返すときは注意してください。オブジェクトリテラルの周りの追加括弧を忘れないでください!

8
user2747594

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'
  }
]

パフォーマンスのための最良の方法ではないかもしれませんが、非常にエレガントです。

1
Youness Abbal
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関数とnon-navite関数の使用の間には常に議論があります。正しく覚えていれば lodash は、キー操作に非ネイティブ関数を使用しているため、アンダースコアよりも高速であると主張しました。

ただし、ブラウザが異なると、結果が非​​常に異なる場合があります。私は常に最高の平均を探しました。

ベンチマークについては、次をご覧ください。

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

1
Dalorzo

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]}
    }, {})
  })
}
1
ssomnoremac