在对象数组中有效地重命名/重新映射javascript / json对象键

时间:2014-01-15 21:17:37

标签: javascript arrays json javascript-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>

...或使用更复杂的东西(虽然更有效率),例如this

这一切都很好,但是如果数组中有200,000个对象怎么办?这是很多处理开销。

是否有更有效的方法来重新映射密钥名称?可能没有遍历整个对象数组?如果您的方法更有效,请提供证明/参考。

8 个答案:

答案 0 :(得分:25)

正如我在评论中已经提到的,如果您可以对对象的值做出某些假设,则可以使用正则表达式来替换键,例如:

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

它不像“干净”,但可能会更快地完成工作。


如果你不得不解析JSON,那么更{结构化的方法'是pass a reviver function to 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

似乎正则表达式(在这种情况下)效率最高,但be careful when trying to parse JSON with regular expressions


测试脚本,加载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");
});

答案 1 :(得分:12)

很久以前问过这个问题,从那时起,我已经习惯于使用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)))

e.g。

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
    }
]
*/

答案 2 :(得分:7)

以下是OP建议使用map()表示清晰(不是表现)。

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

这使用ES6 arrow functions以及当只有一个parm传递给函数且函数体中只有一个语句时可能的快捷语法。

根据您使用各种语言的lambda表达式的历史记录,此表单可能会或可能不会与您产生共鸣。

在使用箭头函数快捷方式语法返回对象文字时要小心。不要忘记对象字面周围的额外数据!

答案 3 :(得分:2)

如果你想让它更可重复使用。也许这是一个不错的方法。

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);

答案 4 :(得分:1)

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 }); });

使用导航功能和非导航功能之间始终存在争议。如果我没记错的话lodash认为它们比下划线更快,因为使用非本机函数进行键操作。

然而,不同的浏览器有时会产生非常不同的结果。我总是寻找最好的平均值。

对于基准测试,您可以看一下:

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

答案 5 :(得分:1)

使用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]}
    }, {})
  })
}

答案 6 :(得分:1)

您可以使用名为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'
  }
]

也许这不是最佳的演奏方式,但它非常优雅。

答案 7 :(得分:0)

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);