Lancé un código para aplanar y desaplanar objetos JSON complejos / anidados. Funciona, pero es un poco lento (activa la advertencia de 'script largo').
Para los nombres planos quiero "." como delimitador e [INDICE] para matrices.
Ejemplos:
un-flattened | flattened
---------------------------
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
[1,[2,[3,4],5],6] => {"[0]":1,"[1].[0]":2,"[1].[1].[0]":3,"[1].[1].[1]":4,"[1].[2]":5,"[2]":6}
Creé un punto de referencia que ~ simula mi caso de uso http://jsfiddle.net/WSzec/
- Obtener un objeto JSON anidado
- Aplanarlo
- Mire a través de él y posiblemente lo modifique mientras está aplanado
- Descomprimirlo de nuevo a su formato original anidado para ser enviado
Me gustaría un código más rápido: para aclarar, el código que completa el punto de referencia JSFiddle ( http://jsfiddle.net/WSzec/ ) significativamente más rápido (~ 20% + sería bueno) en IE 9+, FF 24+ y Chrome 29 +.
Aquí está el código JavaScript relevante: Actual más rápido: http://jsfiddle.net/WSzec/6/
JSON.unflatten = function(data) {
"use strict";
if (Object(data) !== data || Array.isArray(data))
return data;
var result = {}, cur, prop, idx, last, temp;
for(var p in data) {
cur = result, prop = "", last = 0;
do {
idx = p.indexOf(".", last);
temp = p.substring(last, idx !== -1 ? idx : undefined);
cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : {}));
prop = temp;
last = idx + 1;
} while(idx >= 0);
cur[prop] = data[p];
}
return result[""];
}
JSON.flatten = function(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop ? prop+"."+i : ""+i);
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
EDITAR 1 Modificó lo anterior a la implementación de @Bergi que actualmente es la más rápida. Además, el uso de ".indexOf" en lugar de "regex.exec" es aproximadamente un 20% más rápido en FF pero un 20% más lento en Chrome; así que me quedaré con la expresión regular ya que es más simple (aquí está mi intento de usar indexOf para reemplazar la expresión regular http://jsfiddle.net/WSzec/2/ ).
EDITAR 2 Basándome en la idea de @Bergi, logré crear una versión más rápida sin expresiones regulares (3 veces más rápido en FF y ~ 10% más rápido en Chrome). http://jsfiddle.net/WSzec/6/ En esta (la actual) implementación, las reglas para los nombres de clave son simplemente, las claves no pueden comenzar con un número entero o contener un punto.
Ejemplo:
- {"foo": {"bar": [0]}} => {"foo.bar.0": 0}
EDITAR 3 Agregar el enfoque de análisis de ruta en línea de @AaditMShah (en lugar de String.split) ayudó a mejorar el rendimiento no aplanado. Estoy muy contento con la mejora del rendimiento general alcanzada.
Los últimos jsfiddle y jsperf:
fuente
[1].[1].[0]
me parece mal ¿Estás seguro de que este es el resultado deseado?Respuestas:
Aquí está mi implementación mucho más corta:
flatten
no ha cambiado mucho (y no estoy seguro de si realmente necesita esosisEmpty
casos):Juntos, ejecutan su punto de referencia en aproximadamente la mitad del tiempo (Opera 12.16: ~ 900ms en lugar de ~ 1900ms, Chrome 29: ~ 800ms en lugar de ~ 1600ms).
Nota: Esta y la mayoría de las otras soluciones respondidas aquí se centran en la velocidad y son susceptibles a la contaminación del prototipo y no deben usarse en objetos no confiables.
fuente
result === data
no funcionará, nunca son idénticos.Escribí dos funciones
flatten
yunflatten
un objeto JSON.Acoplar un objeto JSON :
Rendimiento :
Desplegar un objeto JSON :
Rendimiento :
Acoplar y desacoplar un objeto JSON :
En general, mi solución funciona igual de bien o incluso mejor que la solución actual.
Rendimiento :
Formato de salida :
Un objeto aplanado usa la notación de puntos para las propiedades del objeto y la notación de paréntesis para los índices de matriz:
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
[1,[2,[3,4],5],6] => {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}
En mi opinión, este formato es mejor que solo usar la notación de puntos:
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a.0.b.0":"c","a.0.b.1":"d"}
[1,[2,[3,4],5],6] => {"0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6}
Ventajas :
desventajas :
La demostración actual de JSFiddle dio los siguientes valores como salida:
Mi demo JSFiddle actualizada dio los siguientes valores como salida:
No estoy realmente seguro de lo que eso significa, así que me quedaré con los resultados de jsPerf. Después de todo, jsPerf es una utilidad de evaluación comparativa de rendimiento. JSFiddle no lo es.
fuente
unflatten({"foo.__proto__.bar": 42})
3 ½ años después ...
Para mi propio proyecto, quería aplanar objetos JSON en notación de puntos mongoDB y se me ocurrió una solución simple:
Características y / o advertencias
{a: () => {}}
¡podría no obtener lo que quería!{a: {}, b: []}
se aplana{}
.fuente
{"x": "abc\"{x}\"yz"}
convierte en lo{ "x": "abc"{,"x",}"yz"}
que no es válido.Versión ES6:
Ejemplo:
fuente
Date
, ¿alguna idea de cómo hacerlo? Por ejemplo, conflatten({a: {b: new Date()}});
Aquí hay otro enfoque que funciona más lento (aproximadamente 1000 ms) que la respuesta anterior, pero tiene una idea interesante :-)
En lugar de recorrer cada cadena de propiedades, solo selecciona la última propiedad y usa una tabla de búsqueda para que el resto almacene los resultados intermedios. Esta tabla de búsqueda se repetirá hasta que no queden cadenas de propiedades y todos los valores residan en propiedades no codificadas.
Actualmente usa el
data
parámetro de entrada para la tabla, y pone muchas propiedades en él; también debería ser posible una versión no destructiva. Tal vez unlastIndexOf
uso inteligente funciona mejor que la expresión regular (depende del motor de expresión regular).Véalo en acción aquí .
fuente
unflatten
el objeto aplanado correctamente. Por ejemplo, considere la matriz[1,[2,[3,4],5],6]
. Suflatten
función aplana este objeto a{"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}
.unflatten
Sin embargo, su función desaplana incorrectamente el objeto aplanado[1,[null,[3,4]],6]
. La razón por la que esto sucede se debe a la declaracióndelete data[p]
que elimina prematuramente el valor intermedio[2,null,5]
antes de[3,4]
agregarlo. Usa una pila para resolverlo. :-)Puedes usar https://github.com/hughsk/flat
Ejemplo del documento
fuente
Este código recursivamente aplana los objetos JSON.
Incluí mi mecanismo de sincronización en el código y me da 1 ms, pero no estoy seguro de si ese es el más preciso.
Salida:
fuente
typeof some === 'object'
es más rápido,some instanceof Object
ya que la primera verificación se realiza en O1, mientras que la segunda en On, donde n es la longitud de una cadena de herencia (el objeto siempre será el último allí).Agregué +/- 10-15% de eficiencia a la respuesta seleccionada mediante una refactorización de código menor y moviendo la función recursiva fuera del espacio de nombres de la función.
Vea mi pregunta: ¿Se reevalúan las funciones de espacio de nombres en cada llamada? por qué esto ralentiza las funciones anidadas.
Ver punto de referencia .
fuente
Aquí está el mío. Se ejecuta en <2 ms en Google Apps Script en un objeto de tamaño considerable. Utiliza guiones en lugar de puntos para los separadores, y no maneja matrices especialmente como en la pregunta del autor de la pregunta, pero esto es lo que quería para mi uso.
Ejemplo:
Salida de ejemplo:
fuente
Usa esta biblioteca:
Uso (de https://www.npmjs.com/package/flat ):
Aplanar:
Sin aplanar:
fuente
Me gustaría agregar una nueva versión de flatten case (esto es lo que necesitaba :)) que, según mis sondas con el jsFiddler anterior, es un poco más rápido que el seleccionado actualmente. Además, personalmente veo este fragmento un poco más legible, lo que, por supuesto, es importante para proyectos de múltiples desarrolladores.
fuente
Aquí hay un código que escribí para aplanar un objeto con el que estaba trabajando. Crea una nueva clase que toma cada campo anidado y lo lleva a la primera capa. Puede modificarlo para que no se aplaste recordando la ubicación original de las teclas. También supone que las claves son únicas incluso en los objetos anidados. Espero eso ayude.
Como ejemplo, convierte
dentro
fuente