Cara tercepat untuk meratakan / un-flatten objek JSON bersarang

159

Saya melemparkan beberapa kode bersama-sama untuk meratakan dan un-meratakan objek JSON kompleks / bersarang. Berhasil, tapi agak lambat (memicu peringatan 'skrip panjang').

Untuk nama yang rata aku mau "." sebagai pembatas dan [INDEKS] untuk array.

Contoh:

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}

Saya membuat patokan yang ~ mensimulasikan use case saya http://jsfiddle.net/WSzec/

  • Dapatkan objek JSON bersarang
  • Ratakan
  • Lihatlah dan mungkin memodifikasinya sambil diratakan
  • Buka ratakan kembali ke format bersarang aslinya untuk dikirim

Saya ingin kode yang lebih cepat: Untuk klarifikasi, kode yang melengkapi tolok ukur JSFiddle ( http://jsfiddle.net/WSzec/ ) secara signifikan lebih cepat (~ 20% + lebih baik) di IE 9+, FF 24+, dan Chrome 29 +.

Berikut kode JavaScript yang relevan: Tercepat Saat Ini: 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;
}

EDIT 1 Dimodifikasi di atas untuk implementasi @Bergi yang saat ini tercepat. Selain itu, menggunakan ".indexOf" bukannya "regex.exec" sekitar 20% lebih cepat dalam FF tetapi 20% lebih lambat di Chrome; jadi saya akan tetap dengan regex karena lebih sederhana (inilah upaya saya menggunakan indexOf untuk mengganti http://exgmail.jsfiddle.net/WSzec/2/ ).

EDIT 2 Membangun ide @Bergi. Saya berhasil membuat versi non-regex yang lebih cepat (3x lebih cepat dalam FF dan ~ 10% lebih cepat di Chrome). http://jsfiddle.net/WSzec/6/ Dalam implementasi ini (saat ini) aturan untuk nama-nama kunci adalah sederhana, kunci tidak dapat dimulai dengan integer atau berisi periode.

Contoh:

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

EDIT 3 Menambahkan pendekatan parsing jalur inline @AaditMShah (daripada String.split) membantu meningkatkan kinerja yang tidak merata. Saya sangat senang dengan peningkatan kinerja yang dicapai secara keseluruhan.

Jsfiddle dan jsperf terbaru:

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

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

Louis Ricci
sumber
7
Tidak ada yang namanya "objek JSON" . Pertanyaannya tampaknya tentang objek JS.
Felix Kling
1
Pertanyaan ini tampaknya lebih sesuai untuk situs Code Review StackExchange: codereview.stackexchange.com
Aadit M Shah
6
@ Feliksling - By JSON object saya maksudkan JS object yang hanya mengandung tipe JavaScript primitif. Anda bisa, misalnya, meletakkan fungsi dalam objek JS, tetapi tidak akan diserialisasi ke JSON - yaitu JSON.stringify ({fn: function () {alert ('a');}}); -
Louis Ricci
2
[1].[1].[0]terlihat salah bagiku. Apakah Anda yakin ini adalah hasil yang diinginkan?
Bergi
2
Sayangnya ada bug: Objek tanggal dikonversi ke JSON kosong.
giacecco

Jawaban:

217

Inilah implementasi saya yang jauh lebih pendek:

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

flattentidak banyak berubah (dan saya tidak yakin apakah Anda benar-benar membutuhkan isEmptykasus - kasus itu):

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

Bersama-sama, mereka menjalankan patokan Anda sekitar setengah dari waktu (Opera 12.16: ~ 900ms bukannya ~ 1900ms, Chrome 29: ~ 800ms bukannya ~ 1600ms).

Catatan: Ini dan sebagian besar solusi lain yang dijawab di sini fokus pada kecepatan dan rentan terhadap polusi prototipe dan tidak akan digunakan pada benda yang tidak dipercaya.

Bergi
sumber
1
Ini bagus! Regex berjalan sangat baik (terutama di Chrome), saya mencoba menggantinya dengan logika indexOf, tetapi hanya mampu mewujudkan kecepatan dalam FF. Saya akan menambahkan hadiah untuk pertanyaan ini untuk melihat apakah peningkatan pintar lain dapat digerakkan, tetapi sejauh ini lebih dari apa yang saya harapkan.
Louis Ricci
1
Saya berhasil menggiling lebih cepat dari implementasi Anda dengan mengganti regex.exec () dengan string.split () dan menyederhanakan format kunci. Saya akan memberikan beberapa hari sebelum saya memberi Anda poin, tetapi saya pikir 'dinding optimasi yang berarti' telah tercapai.
Louis Ricci
JSON.flatten ({}); // {'': {}} - Anda bisa menambahkan baris setelah var result = {}; - jika (hasil === data) mengembalikan data;
Ivan
@Van: Ah, terima kasih untuk kasing tepi itu, meskipun secara semantik sebenarnya akan diperlukan untuk memiliki representasi tambahan untuk objek kosong. Tapi tidak, result === datatidak akan berhasil, mereka tidak pernah identik.
Bergi
@Bergi Ya kamu benar. Object.keys (data) .length === 0 berfungsi
Ivan
26

Saya menulis dua fungsi ke flattendan unflattenobjek JSON.


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

Kinerja :

  1. Ini lebih cepat daripada solusi saat ini di Opera. Solusi saat ini adalah 26% lebih lambat di Opera.
  2. Ini lebih cepat daripada solusi saat ini di Firefox. Solusi saat ini adalah 9% lebih lambat di Firefox.
  3. Ini lebih cepat daripada solusi saat ini di Chrome. Solusi saat ini adalah 29% lebih lambat di Chrome.

Membatalkan objek 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[""];
}

Kinerja :

  1. Ini lebih cepat daripada solusi saat ini di Opera. Solusi saat ini adalah 5% lebih lambat di Opera.
  2. Ini lebih lambat daripada solusi saat ini di Firefox. Solusi saya adalah 26% lebih lambat di Firefox.
  3. Ini lebih lambat daripada solusi saat ini di Chrome. Solusi saya adalah 6% lebih lambat di Chrome.

Ratakan dan hapus merata objek JSON :

Secara keseluruhan solusi saya berkinerja sama baiknya atau bahkan lebih baik daripada solusi saat ini.

Kinerja :

  1. Ini lebih cepat daripada solusi saat ini di Opera. Solusi saat ini adalah 21% lebih lambat di Opera.
  2. Ini secepat solusi saat ini di Firefox.
  3. Ini lebih cepat daripada solusi saat ini di Firefox. Solusi saat ini adalah 20% lebih lambat di Chrome.

Format output :

Objek yang diratakan menggunakan notasi titik untuk properti objek dan notasi braket untuk indeks array:

  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}

Menurut pendapat saya format ini lebih baik daripada hanya menggunakan notasi titik:

  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}

Keuntungan :

  1. Meratakan objek lebih cepat dari solusi saat ini.
  2. Meratakan dan tidak meratakan objek secepat atau lebih cepat dari solusi saat ini.
  3. Objek pipih menggunakan notasi titik dan notasi braket untuk keterbacaan.

Kekurangan :

  1. Membatalkan sebuah objek lebih lambat dari solusi saat ini di sebagian besar (tetapi tidak semua) kasus.

Demo JSFiddle saat ini memberikan nilai-nilai berikut sebagai output:

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

Demo JSFiddle saya yang diperbarui memberikan nilai-nilai berikut sebagai output:

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

Saya tidak begitu yakin apa artinya itu, jadi saya akan tetap dengan hasil jsPerf. Bagaimanapun jsPerf adalah utilitas pembandingan kinerja. JSFiddle tidak.

Aadit M Shah
sumber
Sangat keren. Saya sangat suka gaya untuk meratakan, menggunakan fungsi anonim untuk mendapatkan Array.isArray dan Objek ke lingkup yang lebih dekat. Saya pikir objek tes yang Anda gunakan untuk tes JSPerf terlalu sederhana. Saya membuat objek "fillObj ({}, 4)" di tolok ukur jsfiddle saya untuk meniru kasus nyata dari sepotong besar data bersarang kompleks.
Louis Ricci
Tunjukkan kode untuk objek Anda dan saya akan memasukkannya ke dalam tolok ukur.
Aadit M Shah
2
@LastCoder Hmmm, implementasi Anda saat ini tampaknya lebih cepat dari pada kebanyakan browser saya (terutama Firefox). Menariknya implementasi saya lebih cepat di Opera dan tidak terlalu buruk di Chrome. Saya tidak berpikir memiliki set data yang besar merupakan faktor yang ideal untuk menentukan kecepatan algoritma karena: 1) set data yang besar membutuhkan sejumlah besar memori, pertukaran halaman, dll .; dan itu bukan sesuatu yang dapat Anda kontrol di JS (yaitu Anda berada di tangan browser) 2) jika Anda ingin melakukan pekerjaan intensif CPU maka JS bukan bahasa terbaik. Pertimbangkan untuk menggunakan C sebagai gantinya. Ada perpustakaan JSON untuk C
Aadit M Shah
1
itu poin yang bagus dan memunculkan perbedaan antara benchmarking sintetis vs dunia nyata. Saya senang dengan kinerja JS yang dioptimalkan saat ini, jadi tidak perlu menggunakan C.
Louis Ricci
Implementasi ini juga memiliki bug polusi prototipe, misalnyaunflatten({"foo.__proto__.bar": 42})
Alex Brasetvik
12

3 ½ Tahun kemudian ...

Untuk proyek saya sendiri, saya ingin meratakan objek JSON dalam notasi mongoDB dot dan menghasilkan solusi sederhana:

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

Fitur dan / atau peringatan

  • Hanya menerima objek JSON. Jadi, jika Anda melewati sesuatu seperti {a: () => {}}Anda mungkin tidak mendapatkan apa yang Anda inginkan!
  • Ini menghapus array dan objek kosong. Jadi ini {a: {}, b: []}diratakan {}.
Yan Foto
sumber
1
Bagus, tapi saya tidak mengurus kutipan yang lolos. Jadi {"x": "abc\"{x}\"yz"}menjadi { "x": "abc"{,"x",}"yz"}yang tidak valid.
Simsteve7
@ Simsteve7 Anda benar! Sesuatu yang saya selalu cenderung lupa!
Yan Foto
11

Versi 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 + '.')};
    }, {});
}

Contoh:

console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));
Orang
sumber
1
Saya pikir Anda akan mengalami beberapa kesulitan UNattattening jika Anda tidak memiliki pemisah antara nama properti JSON.stringify (ratakan ({"prop1": 0, "prop2": {"prop3": {"prop3": true, "prop4": "test "}})); ==> {"prop1": 0, "prop2prop3": true, "prop2prop4": "test"} tetapi itu adalah perbaikan yang mudah, singkatnya sintaks ES6 benar-benar bagus
Louis Ricci
Itu sangat benar, separator menambahkan
Guy
Ini tidak cocok dengan Anda Date, ada ide bagaimana cara melakukannya? Misalnya, denganflatten({a: {b: new Date()}});
Ehtesh Choudhury
Anda dapat menggunakan cap waktu: {b: Tanggal baru (). GetTime ()}} dan kemudian kembalikan ke tanggal dengan Tanggal baru (cap waktu)
Guy
6

Berikut ini pendekatan lain yang berjalan lebih lambat (sekitar 1000 ms) dari jawaban di atas, tetapi memiliki ide yang menarik :-)

Alih-alih mengulangi setiap rantai properti, ia hanya mengambil properti terakhir dan menggunakan tabel pencarian untuk sisanya untuk menyimpan hasil antara. Tabel pencarian ini akan diulangi sampai tidak ada rantai properti yang tersisa dan semua nilai berada pada properti yang tidak dipatenkan.

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

Saat ini menggunakan dataparameter input untuk tabel, dan menempatkan banyak properti di atasnya - versi non-destruktif juga dimungkinkan. Mungkin lastIndexOfpenggunaan cerdas berkinerja lebih baik daripada regex (tergantung pada mesin regex).

Lihat beraksi di sini .

Bergi
sumber
Saya tidak mencatat jawaban Anda. Namun saya ingin menunjukkan bahwa fungsi Anda tidak unflattenobjek rata dengan benar. Sebagai contoh, pertimbangkan array [1,[2,[3,4],5],6]. flattenFungsi Anda meratakan objek ini ke {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}. unflattenNamun, fungsi Anda secara keliru membatalkan objek rata [1,[null,[3,4]],6]. Alasan ini terjadi adalah karena pernyataan delete data[p]yang secara prematur menghapus nilai menengah [2,null,5]sebelum [3,4]ditambahkan ke dalamnya. Gunakan tumpukan untuk menyelesaikannya. :-)
Aadit M Shah
1
Ah, begitu, urutan enumerasi yang tidak ditentukan ... Akan memperbaikinya dengan antrian properti, silakan masukkan solusi stack Anda dalam jawaban sendiri. Terima kasih atas petunjuknya!
Bergi
4

Anda dapat menggunakan https://github.com/hughsk/flat

Ambil objek Javascript yang bersarang dan ratakan, atau hapus objek dengan kunci yang dibatasi.

Contoh dari dok

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
//         }
//     }
// }
Tom Esterez
sumber
1
Bagaimana Anda menggunakan ini di AngularJS?
kensplanet
2

Kode ini secara mendatar meratakan objek JSON.

Saya memasukkan mekanisme pengaturan waktu saya dalam kode dan memberi saya 1 ms tetapi saya tidak yakin apakah itu yang paling akurat.

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

Keluaran:

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
sumber
1
Saya pikir, itu typeof some === 'object'lebih cepat maka some instanceof Objectsejak pemeriksaan pertama dilakukan di O1 sementara yang kedua di Di mana n adalah panjang rantai pewarisan (Objek akan selalu menjadi yang terakhir di sana).
GullerYA
1

Saya menambahkan +/- 10-15% efisiensi untuk jawaban yang dipilih oleh refactoring kode minor dan memindahkan fungsi rekursif di luar namespace fungsi.

Lihat pertanyaan saya: Apakah fungsi namespace dievaluasi kembali pada setiap panggilan? untuk alasan ini memperlambat fungsi bersarang.

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

Lihat patokan .

jtrumbull
sumber
1

Ini milik saya. Ini berjalan dalam <2ms dalam Google Apps Script pada objek yang cukup besar. Ini menggunakan tanda hubung bukan titik untuk separator, dan tidak menangani array khusus seperti dalam pertanyaan penanya, tapi ini yang saya inginkan untuk saya gunakan.

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

Contoh:

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

Contoh output:

[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
sumber
1

Gunakan perpustakaan ini:

npm install flat

Penggunaan (dari https://www.npmjs.com/package/flat ):

Meratakan:

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

Batalkan rata:

var unflatten = require('flat').unflatten

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

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }
ons
sumber
2
Untuk melengkapi jawaban Anda, Anda harus menambahkan contoh cara menggunakan perpustakaan itu.
António Almeida
0

Saya ingin menambahkan versi baru case rata (ini yang saya butuhkan :)) yang, menurut penyelidikan saya dengan jsFiddler di atas, sedikit lebih cepat daripada yang saat ini dipilih. Selain itu, saya pribadi melihat cuplikan ini sedikit lebih mudah dibaca, yang tentu saja penting untuk proyek multi-pengembang.

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
sumber
0

Berikut adalah beberapa kode yang saya tulis untuk meratakan objek yang sedang saya kerjakan. Itu menciptakan kelas baru yang mengambil setiap bidang bersarang dan membawanya ke lapisan pertama. Anda dapat memodifikasinya menjadi tidak rata dengan mengingat penempatan kunci yang asli. Ini juga mengasumsikan kunci unik bahkan di seluruh objek bersarang. Semoga ini bisa membantu.

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

Sebagai contoh, itu mengkonversi

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

ke

{ a: 0, b: 1, c: 2 }
Imran Q
sumber