Самый быстрый способ сглаживать / не сглаживать вложенные объекты JSON

159

Я собрал некоторый код, чтобы сгладить и разровнять сложные / вложенные объекты JSON. Это работает, но это немного медленно (вызывает предупреждение «длинный скрипт»).

Для сглаженных имен я хочу "." в качестве разделителя и [INDEX] для массивов.

Примеры:

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}

Я создал бенчмарк, который ~ симулирует мой вариант использования http://jsfiddle.net/WSzec/

  • Получить вложенный объект JSON
  • Свести это
  • Просмотрите его и, возможно, измените, пока он сплющен
  • Разблокируйте его обратно в исходный вложенный формат для отправки

Я хотел бы более быстрый код: для пояснения, код, который завершает тест JSFiddle ( http://jsfiddle.net/WSzec/ ) значительно быстрее (было бы неплохо ~ 20% +) в IE 9+, FF 24+ и Chrome 29 +.

Вот соответствующий код JavaScript: Текущий самый быстрый: 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;
}

РЕДАКТИРОВАТЬ 1 Изменено выше, чтобы реализация @Bergi, которая в настоящее время является самой быстрой. Кроме того, использование «.indexOf» вместо «regex.exec» примерно на 20% быстрее в FF, но на 20% медленнее в Chrome; поэтому я буду придерживаться регулярного выражения, поскольку это проще (вот моя попытка использовать indexOf для замены регулярного выражения http://jsfiddle.net/WSzec/2/ ).

РЕДАКТИРОВАТЬ 2 Опираясь на идею @Bergi, мне удалось создать более быструю версию без регулярных выражений (в 3 раза быстрее в FF и на 10% быстрее в Chrome). http://jsfiddle.net/WSzec/6/ В этой (текущей) реализации правила для имен ключей просты: ключи не могут начинаться с целого числа или содержать точку.

Пример:

  • {"foo": {"bar": [0]}} => {"foo.bar.0": 0}

РЕДАКТИРОВАТЬ 3 Добавление подхода @AaditMShah для анализа встроенных путей (а не String.split) помогло улучшить производительность без обратной связи. Я очень доволен достигнутым общим улучшением производительности.

Последние версии jsfiddle и jsperf:

http://jsfiddle.net/WSzec/14/

http://jsperf.com/flatten-un-flatten/4

Луи Риччи
источник
7
Не существует такого понятия, как «объект JSON» . Вопрос, кажется, об объектах JS.
Феликс Клинг
1
Этот вопрос представляется более подходящим для сайта StackExchange
Аадит М Шах,
6
@FelixKling - Под объектом JSON я подразумевал объекты JS, которые содержат только примитивные типы JavaScript. Вы можете, например, поместить функцию в объект JS, но она не будет сериализована в JSON - т.е. JSON.stringify ({fn: function () {alert ('a');}}); -
Луи Риччи
2
[1].[1].[0]выглядит неправильно для меня. Вы уверены, что это желаемый результат?
Берги
2
К сожалению, есть ошибка: объекты Date конвертируются в пустой JSON.
giacecco

Ответы:

217

Вот моя гораздо более короткая реализация:

Object.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
        resultholder = {};
    for (var p in data) {
        var cur = resultholder,
            prop = "",
            m;
        while (m = regex.exec(p)) {
            cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
            prop = m[2] || m[1];
        }
        cur[prop] = data[p];
    }
    return resultholder[""] || resultholder;
};

flattenне сильно изменился (и я не уверен, действительно ли вам нужны эти isEmptyслучаи):

Object.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 + "[" + 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 && prop)
                result[prop] = {};
        }
    }
    recurse(data, "");
    return result;
}

Вместе они запускают ваш тест примерно в половине случаев (Opera 12.16: ~ 900 мс вместо ~ 1900 мс, Chrome 29: ~ 800 мс вместо ~ 1600 мс).

Примечание. Это и большинство других решений, на которые даны ответы, фокусируются на скорости и подвержены загрязнению прототипа и не должны использоваться на ненадежных объектах.

Берги
источник
1
Это круто! Регулярное выражение работает замечательно (особенно в Chrome), я попытался заменить его логикой indexOf, но смог реализовать ускорение только в FF. Я добавлю награду к этому вопросу, чтобы увидеть, можно ли добиться очередного умного улучшения, но пока это больше, чем я надеялся.
Луи Риччи
1
Мне удалось увеличить скорость вашей реализации, заменив regex.exec () на string.split () и упростив формат ключа. Я дам ему несколько дней, прежде чем присудить вам очки, но я думаю, что «стена значимой оптимизации» достигнута.
Луи Риччи
JSON.flatten ({}); // {'': {}} - вы можете добавить строку после var result = {}; - если (результат === данные) вернуть данные;
Иван
@Ivan: Ах, спасибо за тот крайний случай, хотя семантически это фактически потребовало бы иметь дополнительное представление для пустых объектов. Но нет, result === dataне сработает, они никогда не идентичны.
Берги
@ Берги Да, ты прав. Object.keys (data) .length === 0 работает, хотя
Иван
26

Я написал две функции flattenи unflattenобъект JSON.


Свести объект JSON :

var flatten = (function (isArray, wrapped) {
    return function (table) {
        return reduce("", {}, table);
    };

    function reduce(path, accumulator, table) {
        if (isArray(table)) {
            var length = table.length;

            if (length) {
                var index = 0;

                while (index < length) {
                    var property = path + "[" + index + "]", item = table[index++];
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            } else accumulator[path] = table;
        } else {
            var empty = true;

            if (path) {
                for (var property in table) {
                    var item = table[property], property = path + "." + property, empty = false;
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            } else {
                for (var property in table) {
                    var item = table[property], empty = false;
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            }

            if (empty) accumulator[path] = table;
        }

        return accumulator;
    }
}(Array.isArray, Object));

Производительность :

  1. Это быстрее, чем текущее решение в Opera. Текущее решение на 26% медленнее в Opera.
  2. Это быстрее, чем текущее решение в Firefox. Текущее решение на 9% медленнее в Firefox.
  3. Это быстрее, чем текущее решение в Chrome. Текущее решение на 29% медленнее в Chrome.

Разблокируйте объект JSON :

function unflatten(table) {
    var result = {};

    for (var path in table) {
        var cursor = result, length = path.length, property = "", index = 0;

        while (index < length) {
            var char = path.charAt(index);

            if (char === "[") {
                var start = index + 1,
                    end = path.indexOf("]", start),
                    cursor = cursor[property] = cursor[property] || [],
                    property = path.slice(start, end),
                    index = end + 1;
            } else {
                var cursor = cursor[property] = cursor[property] || {},
                    start = char === "." ? index + 1 : index,
                    bracket = path.indexOf("[", start),
                    dot = path.indexOf(".", start);

                if (bracket < 0 && dot < 0) var end = index = length;
                else if (bracket < 0) var end = index = dot;
                else if (dot < 0) var end = index = bracket;
                else var end = index = bracket < dot ? bracket : dot;

                var property = path.slice(start, end);
            }
        }

        cursor[property] = table[path];
    }

    return result[""];
}

Производительность :

  1. Это быстрее, чем текущее решение в Opera. Текущее решение на 5% медленнее в Opera.
  2. Это медленнее, чем текущее решение в Firefox. Мое решение медленнее на 26% в Firefox.
  3. Это медленнее, чем текущее решение в Chrome. Мое решение на 6% медленнее в Chrome.

Выровнять и развернуть объект JSON :

В целом, мое решение работает так же хорошо или даже лучше, чем текущее решение.

Производительность :

  1. Это быстрее, чем текущее решение в Opera. Текущее решение на 21% медленнее в Opera.
  2. Это так же быстро, как текущее решение в Firefox.
  3. Это быстрее, чем текущее решение в Firefox. Текущее решение на 20% медленнее в Chrome.

Выходной формат :

Плоский объект использует точечную нотацию для свойств объекта и скобочную нотацию для индексов массива:

  1. {foo:{bar:false}} => {"foo.bar":false}
  2. {a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
  3. [1,[2,[3,4],5],6] => {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}

На мой взгляд, этот формат лучше, чем только использование точечной нотации:

  1. {foo:{bar:false}} => {"foo.bar":false}
  2. {a:[{b:["c","d"]}]} => {"a.0.b.0":"c","a.0.b.1":"d"}
  3. [1,[2,[3,4],5],6] => {"0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6}

Преимущества :

  1. Уплощение объекта происходит быстрее, чем текущее решение.
  2. Выравнивание и разглаживание объекта происходит так же быстро, как и текущее решение.
  3. Для удобства чтения плоские объекты используют как точечную, так и скобочную нотацию.

Недостатки :

  1. В большинстве (но не во всех) случаях раскрытие объекта происходит медленнее, чем текущее решение.

Текущая демонстрация JSFiddle выдает следующие значения в качестве вывода:

Nested : 132175 : 63
Flattened : 132175 : 564
Nested : 132175 : 54
Flattened : 132175 : 508

Моя обновленная демоверсия JSFiddle выдала следующие значения:

Nested : 132175 : 59
Flattened : 132175 : 514
Nested : 132175 : 60
Flattened : 132175 : 451

Я не совсем уверен, что это значит, поэтому я буду придерживаться результатов jsPerf. В конце концов, jsPerf - это утилита для измерения производительности. JSFiddle нет.

Адит М Шах
источник
Очень круто. Мне действительно нравится стиль для flatten, использующий анонимные функции, чтобы Array.isArray и Object стали ближе. Я думаю, что тестовый объект, который вы используете для теста JSPerf, слишком прост. Я создал объект "fillObj ({}, 4)" в своем тесте jsfiddle, чтобы имитировать реальный случай большого сложного вложенного фрагмента данных.
Луи Риччи
Покажите мне код для вашего объекта, и я включу его в тест.
Аадит М Шах
2
@LastCoder Хммм, ваша текущая реализация кажется быстрее, чем моя, в большинстве браузеров (особенно Firefox). Интересно, что моя реализация быстрее в Opera, и в Chrome все не так плохо. Я не думаю, что наличие такого большого набора данных является идеальным фактором для определения скорости алгоритма, потому что: 1) большие наборы данных требуют большого объема памяти, подкачки страниц и т. Д .; и это не то, что вы можете контролировать в JS (то есть вы находитесь в зависимости от браузера) 2) если вы хотите выполнять нагрузку на процессор, то JS не лучший язык. Попробуйте использовать C вместо. Есть библиотеки JSON для C
Aadit M Shah
1
Это хороший момент, который поднимает разницу между синтетическим и реальным сравнительным тестированием. Я доволен производительностью текущего оптимизированного JS, поэтому не нужно использовать C.
Луи Риччи
В этой реализации также есть ошибка загрязнения прототипа, напримерunflatten({"foo.__proto__.bar": 42})
Алекс Брасетвик
12

3 с половиной года спустя ...

Для моего собственного проекта я хотел сгладить объекты JSON в точечной нотации mongoDB и предложил простое решение:

/**
 * Recursively flattens a JSON object using dot notation.
 *
 * NOTE: input must be an object as described by JSON spec. Arbitrary
 * JS objects (e.g. {a: () => 42}) may result in unexpected output.
 * MOREOVER, it removes keys with empty objects/arrays as value (see
 * examples bellow).
 *
 * @example
 * // returns {a:1, 'b.0.c': 2, 'b.0.d.e': 3, 'b.1': 4}
 * flatten({a: 1, b: [{c: 2, d: {e: 3}}, 4]})
 * // returns {a:1, 'b.0.c': 2, 'b.0.d.e.0': true, 'b.0.d.e.1': false, 'b.0.d.e.2.f': 1}
 * flatten({a: 1, b: [{c: 2, d: {e: [true, false, {f: 1}]}}]})
 * // return {a: 1}
 * flatten({a: 1, b: [], c: {}})
 *
 * @param obj item to be flattened
 * @param {Array.string} [prefix=[]] chain of prefix joined with a dot and prepended to key
 * @param {Object} [current={}] result of flatten during the recursion
 *
 * @see https://docs.mongodb.com/manual/core/document/#dot-notation
 */
function flatten (obj, prefix, current) {
  prefix = prefix || []
  current = current || {}

  // Remember kids, null is also an object!
  if (typeof (obj) === 'object' && obj !== null) {
    Object.keys(obj).forEach(key => {
      this.flatten(obj[key], prefix.concat(key), current)
    })
  } else {
    current[prefix.join('.')] = obj
  }

  return current
}

Особенности и / или предостережения

  • Он принимает только объекты JSON. Так что, если вы пропустите что-то подобное, {a: () => {}}вы можете не получить то, что хотели!
  • Удаляет пустые массивы и объекты. Так что это {a: {}, b: []}сглаживается {}.
Ян Фото
источник
1
Хорошо, но я не забочусь о кавычках. Так {"x": "abc\"{x}\"yz"}становится { "x": "abc"{,"x",}"yz"}недействительным.
Simsteve7
@ Simsteve7 ты прав! То, что я всегда склонен забывать!
Ян Фото
11

Версия ES6:

const flatten = (obj, path = '') => {        
    if (!(obj instanceof Object)) return {[path.replace(/\.$/g, '')]:obj};

    return Object.keys(obj).reduce((output, key) => {
        return obj instanceof Array ? 
             {...output, ...flatten(obj[key], path +  '[' + key + '].')}:
             {...output, ...flatten(obj[key], path + key + '.')};
    }, {});
}

Пример:

console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));
парень
источник
1
Я думаю, у вас возникнут некоторые трудности с развертыванием, если у вас нет разделителей между именами свойств JSON.stringify (flatten ({"prop1": 0, "prop2": {"prop3": true, "prop4": "test «}})); ==> {"prop1": 0, "prop2prop3": true, "prop2prop4": "test"}, но это легко исправить, краткость синтаксиса ES6 действительно хороша
Луи Риччи
Это очень верно, добавлены разделители
Guy
Это не очень приятно Date, есть идеи, как заставить это сделать? Например, сflatten({a: {b: new Date()}});
Эхтеш Чоудхури
Вы можете использовать отметки времени: {b: new Date (). GetTime ()}} и позже вернуть его к дате с новой датой (отметка времени)
Guy
6

Вот еще один подход, который работает медленнее (около 1000 мс), чем приведенный выше ответ, но имеет интересную идею :-)

Вместо итерации по каждой цепочке свойств, он просто выбирает последнее свойство и использует справочную таблицу для остальных, чтобы сохранить промежуточные результаты. Эта справочная таблица будет повторяться до тех пор, пока не останется цепочек свойств, а все значения будут находиться в некокатированных свойствах.

JSON.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)$|\[(\d+)\]$/,
        props = Object.keys(data),
        result, p;
    while(p = props.shift()) {
        var m = regex.exec(p),
            target;
        if (m.index) {
            var rest = p.slice(0, m.index);
            if (!(rest in data)) {
                data[rest] = m[2] ? [] : {};
                props.push(rest);
            }
            target = data[rest];
        } else {
            target = result || (result = (m[2] ? [] : {}));
        }
        target[m[2] || m[1]] = data[p];
    }
    return result;
};

В настоящее время он использует dataвходной параметр для таблицы и накладывает на него множество свойств - также возможна неразрушающая версия. Может быть, умное lastIndexOfиспользование работает лучше, чем регулярное выражение (зависит от движка регулярных выражений).

Смотрите это в действии здесь .

Берги
источник
Я не понизил ваш ответ. Однако я хотел бы отметить, что ваша функция unflattenнеправильно выравнивает объект. Например, рассмотрим массив [1,[2,[3,4],5],6]. Ваша flattenфункция выравнивает этот объект до {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}. Ваша unflattenфункция, однако, неправильно раскрывает сплющенный объект до [1,[null,[3,4]],6]. Причина этого заключается в том, delete data[p]что оператор преждевременно удаляет промежуточное значение [2,null,5]перед [3,4]его добавлением к нему. Используйте стек, чтобы решить это. :-)
Aadit M Shah
1
Ах, я вижу, неопределенный порядок перечисления ... Собираюсь исправить это с помощью очереди свойств, пожалуйста, поместите свое решение стека в собственный ответ. Спасибо за подсказку!
Берги
4

Вы можете использовать https://github.com/hughsk/flat

Возьмите вложенный объект Javascript и сгладьте его или отмените объект с помощью ключей с разделителями.

Пример из документа

var flatten = require('flat')

flatten({
    key1: {
        keyA: 'valueI'
    },
    key2: {
        keyB: 'valueII'
    },
    key3: { a: { b: { c: 2 } } }
})

// {
//   'key1.keyA': 'valueI',
//   'key2.keyB': 'valueII',
//   'key3.a.b.c': 2
// }


var unflatten = require('flat').unflatten

unflatten({
    'three.levels.deep': 42,
    'three.levels': {
        nested: true
    }
})

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }
Том Эстерез
источник
1
Как вы используете это в AngularJS?
kensplanet
2

Этот код рекурсивно выравнивает объекты JSON.

Я включил свой механизм синхронизации в код, и он дает мне 1 мс, но я не уверен, что это самый точный.

            var new_json = [{
              "name": "fatima",
              "age": 25,
              "neighbour": {
                "name": "taqi",
                "location": "end of the street",
                "property": {
                  "built in": 1990,
                  "owned": false,
                  "years on market": [1990, 1998, 2002, 2013],
                  "year short listed": [], //means never
                }
              },
              "town": "Mountain View",
              "state": "CA"
            },
            {
              "name": "qianru",
              "age": 20,
              "neighbour": {
                "name": "joe",
                "location": "opposite to the park",
                "property": {
                  "built in": 2011,
                  "owned": true,
                  "years on market": [1996, 2011],
                  "year short listed": [], //means never
                }
              },
              "town": "Pittsburgh",
              "state": "PA"
            }]

            function flatten(json, flattened, str_key) {
                for (var key in json) {
                  if (json.hasOwnProperty(key)) {
                    if (json[key] instanceof Object && json[key] != "") {
                      flatten(json[key], flattened, str_key + "." + key);
                    } else {
                      flattened[str_key + "." + key] = json[key];
                    }
                  }
                }
            }

        var flattened = {};
        console.time('flatten'); 
        flatten(new_json, flattened, "");
        console.timeEnd('flatten');

        for (var key in flattened){
          console.log(key + ": " + flattened[key]);
        }

Вывод:

flatten: 1ms
.0.name: fatima
.0.age: 25
.0.neighbour.name: taqi
.0.neighbour.location: end of the street
.0.neighbour.property.built in: 1990
.0.neighbour.property.owned: false
.0.neighbour.property.years on market.0: 1990
.0.neighbour.property.years on market.1: 1998
.0.neighbour.property.years on market.2: 2002
.0.neighbour.property.years on market.3: 2013
.0.neighbour.property.year short listed: 
.0.town: Mountain View
.0.state: CA
.1.name: qianru
.1.age: 20
.1.neighbour.name: joe
.1.neighbour.location: opposite to the park
.1.neighbour.property.built in: 2011
.1.neighbour.property.owned: true
.1.neighbour.property.years on market.0: 1996
.1.neighbour.property.years on market.1: 2011
.1.neighbour.property.year short listed: 
.1.town: Pittsburgh
.1.state: PA
sfrizvi6
источник
1
Я думаю, это typeof some === 'object'быстрее, чем some instanceof Objectпервая проверка в O1, а вторая в On, где n - длина цепочки наследования (Object всегда будет последней).
GullerYA
1

Я добавил +/- 10-15% эффективности к выбранному ответу путем незначительного рефакторинга кода и перемещения рекурсивной функции за пределы пространства имен функции.

Смотри мой вопрос: переоцениваются ли функции пространства имен при каждом вызове? почему это замедляет вложенные функции.

function _flatten (target, obj, path) {
  var i, empty;
  if (obj.constructor === Object) {
    empty = true;
    for (i in obj) {
      empty = false;
      _flatten(target, obj[i], path ? path + '.' + i : i);
    }
    if (empty && path) {
      target[path] = {};
    }
  } 
  else if (obj.constructor === Array) {
    i = obj.length;
    if (i > 0) {
      while (i--) {
        _flatten(target, obj[i], path + '[' + i + ']');
      }
    } else {
      target[path] = [];
    }
  }
  else {
    target[path] = obj;
  }
}

function flatten (data) {
  var result = {};
  _flatten(result, data, null);
  return result;
}

Смотрите бенчмарк .

jtrumbull
источник
1

Вот мой. Он запускается в <2ms в скрипте Google Apps для значительного объекта. Он использует тире вместо точек для разделителей, и он не обрабатывает массивы, как в вопросе Аскера, но это то, что я хотел для моего использования.

function flatten (obj) {
  var newObj = {};
  for (var key in obj) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      var temp = flatten(obj[key])
      for (var key2 in temp) {
        newObj[key+"-"+key2] = temp[key2];
      }
    } else {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Пример:

var test = {
  a: 1,
  b: 2,
  c: {
    c1: 3.1,
    c2: 3.2
  },
  d: 4,
  e: {
    e1: 5.1,
    e2: 5.2,
    e3: {
      e3a: 5.31,
      e3b: 5.32
    },
    e4: 5.4
  },
  f: 6
}

Logger.log("start");
Logger.log(JSON.stringify(flatten(test),null,2));
Logger.log("done");

Пример вывода:

[17-02-08 13:21:05:245 CST] start
[17-02-08 13:21:05:246 CST] {
  "a": 1,
  "b": 2,
  "c-c1": 3.1,
  "c-c2": 3.2,
  "d": 4,
  "e-e1": 5.1,
  "e-e2": 5.2,
  "e-e3-e3a": 5.31,
  "e-e3-e3b": 5.32,
  "e-e4": 5.4,
  "f": 6
}
[17-02-08 13:21:05:247 CST] done
paulwal222
источник
1

Используйте эту библиотеку:

npm install flat

Использование (с https://www.npmjs.com/package/flat ):

Свести:

    var flatten = require('flat')


    flatten({
        key1: {
            keyA: 'valueI'
        },
        key2: {
            keyB: 'valueII'
        },
        key3: { a: { b: { c: 2 } } }
    })

    // {
    //   'key1.keyA': 'valueI',
    //   'key2.keyB': 'valueII',
    //   'key3.a.b.c': 2
    // }

Un-выравниваться:

var unflatten = require('flat').unflatten

unflatten({
    'three.levels.deep': 42,
    'three.levels': {
        nested: true
    }
})

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }
унция
источник
2
Чтобы завершить свой ответ, вы должны добавить пример использования этой библиотеки.
Антонио Алмейда
0

Я хотел бы добавить новую версию flatten case (это то, что мне было нужно :)), которая, согласно моим исследованиям с вышеупомянутым jsFiddler, немного быстрее, чем текущая выбранная. Более того, я лично вижу этот фрагмент немного более читабельным, что, конечно, важно для проектов с несколькими разработчиками.

function flattenObject(graph) {
    let result = {},
        item,
        key;

    function recurr(graph, path) {
        if (Array.isArray(graph)) {
            graph.forEach(function (itm, idx) {
                key = path + '[' + idx + ']';
                if (itm && typeof itm === 'object') {
                    recurr(itm, key);
                } else {
                    result[key] = itm;
                }
            });
        } else {
            Reflect.ownKeys(graph).forEach(function (p) {
                key = path + '.' + p;
                item = graph[p];
                if (item && typeof item === 'object') {
                    recurr(item, key);
                } else {
                    result[key] = item;
                }
            });
        }
    }
    recurr(graph, '');

    return result;
}
GullerYA
источник
0

Вот некоторый код, который я написал, чтобы сгладить объект, с которым я работал. Он создает новый класс, который берет каждое вложенное поле и переносит его на первый слой. Вы можете изменить его так, чтобы он был открыт, запомнив оригинальное расположение клавиш. Также предполагается, что ключи уникальны даже для вложенных объектов. Надеюсь, поможет.

class JSONFlattener {
    ojson = {}
    flattenedjson = {}

    constructor(original_json) {
        this.ojson = original_json
        this.flattenedjson = {}
        this.flatten()
    }

    flatten() {
        Object.keys(this.ojson).forEach(function(key){
            if (this.ojson[key] == null) {

            } else if (this.ojson[key].constructor == ({}).constructor) {
                this.combine(new JSONFlattener(this.ojson[key]).returnJSON())
            } else {
                this.flattenedjson[key] = this.ojson[key]
            }
        }, this)        
    }

    combine(new_json) {
        //assumes new_json is a flat array
        Object.keys(new_json).forEach(function(key){
            if (!this.flattenedjson.hasOwnProperty(key)) {
                this.flattenedjson[key] = new_json[key]
            } else {
                console.log(key+" is a duplicate key")
            }
        }, this)
    }

    returnJSON() {
        return this.flattenedjson
    }
}

console.log(new JSONFlattener(dad_dictionary).returnJSON())

Как пример, он преобразует

nested_json = {
    "a": {
        "b": {
            "c": {
                "d": {
                    "a": 0
                }
            }
        }
    },
    "z": {
        "b":1
    },
    "d": {
        "c": {
            "c": 2
        }
    }
}

в

{ a: 0, b: 1, c: 2 }
Имран К
источник