Renommer / re-map efficacement les clés d'objet javascript/json dans le tableau des objets

j'ai des données JSON structurées comme ça. Supposons que ce soit interchangeable, via JSON.parse() :

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

j'ai besoin qu'il ressemble à ceci, remappant title à name , et uid à id avec le résultat:

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

la façon la plus évidente de le faire est comme ceci:

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>

...ou en utilisant quelque chose de plus complexe (bien que pas plus efficace) comme ce .

c'est très bien, mais s'il y avait 200 000 objets dans le tableau? C'est beaucoup de la charge de traitement.

y a-t-il un moyen plus efficace de remaper un nom de clé? Peut-être sans boucler tout le réseau d'objets? Si votre méthode est plus efficace, veuillez fournir la preuve/références.

20
demandé sur gman 2014-01-16 01:17:36

7 réponses

Comme je l'ai déjà mentionné dans les commentaires, si vous pouvez faire certaines hypothèses sur les valeurs des objets, vous pouvez utiliser une expression régulière pour remplacer les clés, par exemple:

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

Ce n'est pas aussi "propre", mais il pourrait faire le travail plus rapidement.


si vous devez de toute façon analyser le JSON, une approche plus structurée serait de passer un fonction de réveil pour JSON.parse et vous pourriez être en mesure d'éviter une passe supplémentaire sur le tableau. Cela dépend probablement de la façon dont le moteur implémente JSON.parse cependant (peut-être qu'ils parsèment toute la chaîne d'abord et ensuite faire un second passage avec la fonction de reviver, dans ce cas, vous ne obtiendriez aucun avantage).

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

"151960920 des" points de référence, en utilisant le Nœud.js script ci-dessous pour tester 3 fois:

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

il apparaît comme si le regex (dans ce cas) est le plus efficace, mais soyez prudent lorsque vous essayez de parser JSON avec des expressions régulières .


script de Test, le chargement 100,230 lignes de l'OP de l'échantillon 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");
});
24
répondu Felix Kling 2017-05-23 12:25:31

a posé cette question il y a longtemps, et depuis lors, j'ai pris l'habitude d'utiliser le tableau .prototype.map() pour faire le travail, plus pour la stabilité et la propreté du code de la performance. Bien qu'il ne soit certainement pas le plus performant, il semble grand:

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

si vous avez besoin d'une fonction plus flexible (et compatible ES6), essayez:

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

p.ex.

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
    }
]
*/
9
répondu brandonscript 2017-07-11 22:15:47

voici un autre point de vue sur la suggestion de L'OP d'utiliser map() par souci de clarté (et non de performance).

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

utilise fonctions de flèche ES6 et les syntaxes à raccourci qui sont possibles quand il n'y a qu'un seul Parme passé à la fonction et une seule instruction dans le corps de la fonction.

selon votre histoire avec des expressions lambda dans diverses langues, cette forme peut ou non résonner avec vous.

soyez prudent en retournant un objet littéral dans la syntaxe de raccourci de la fonction de flèche comme ceci. N'oubliez pas les parens supplémentaires autour de l'objet littéral!

7
répondu user2747594 2016-06-24 21:01:51

Si vous voulez le rendre un peu plus réutilisable. C'est peut-être un décent approche.

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);
2
répondu luwes 2015-06-25 17:40:19
var jsonObj = [/*sample array in question*/   ]

basé sur différents benchmarks discutés ci-dessous, la solution la plus rapide est native pour:

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

je pense alternativement sans utiliser de cadres ce sera l'option 2:

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

il y a toujours un débat entre l'utilisation des fonctions navite et non-navite. Si je me souviens bien lodash ont fait valoir qu'ils étaient plus rapide que underscore parce que l'utilisation des fonctions non natives pour les opérations clés.

Toutefois différents navigateurs produisent parfois des résultats très différents. J'ai toujours cherché le meilleur moyen.

pour les benchmarks vous pouvez jeter un oeil à ceci:

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

1
répondu Dalorzo 2014-01-15 21:53:42

utilisant 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
    }, {})
  })
}

utilisant 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
répondu ssomnoremac 2017-07-11 21:22:37

Vous pouvez utiliser un mécanisme national de prévention package nommé nœud-données-transformer .

vos données:

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

Votre mapping :

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

et utiliser le paquet:

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

résultat:

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

ce n'est peut-être pas la meilleure façon de jouer, mais c'est assez élégant.

1
répondu Youness Abbal 2018-07-16 15:48:07