İç içe JSON nesnelerini düzleştirmenin / düzleştirmenin en hızlı yolu


159

Karmaşık / iç içe JSON nesnelerini düzleştirmek ve düzleştirmek için bazı kodları birlikte attım. Çalışıyor, ancak biraz yavaş ('uzun komut dosyası' uyarısını tetikler).

Düzleştirilmiş isimler için "" istiyorum. diziler için sınırlayıcı ve [INDEX] gibi.

Örnekler:

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}

Kullanım durumumu simüle eden bir karşılaştırma ölçütü oluşturdum http://jsfiddle.net/WSzec/

  • Yuvalanmış bir JSON nesnesi edinin
  • Düzleştir
  • İçine bakın ve düzleştirilmiş durumdayken değiştirin
  • Gönderilecek orijinal iç içe biçimine geri getirin

Daha hızlı kod istiyorum: Açıklama için IE 9+, FF 24+ ve Chrome 29'da JSFiddle karşılaştırmasını ( http://jsfiddle.net/WSzec/ ) önemli ölçüde daha hızlı (~% 20 + iyi olurdu) tamamlayan kod +.

İlgili JavaScript kodu şöyledir: En Hızlı: 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 Yukarıdakileri, şu anda en hızlı olan @Bergi uygulamasına değiştirdi. Bir yana, "regex.exec" yerine ".indexOf" kullanmak FF'de yaklaşık% 20, Chrome'da% 20 daha yavaştır; bu yüzden daha basit olduğu için normal ifadeye sadık kalacağım (burada, http://jsfiddle.net/WSzec/2/ normal ifadesinin yerine indexOf kullanma girişimim ).

EDIT 2 @Bergi'nin fikrine dayanarak daha hızlı regex olmayan bir sürüm oluşturmayı başardım (FF'de 3 kat daha hızlı ve Chrome'da ~% 10 daha hızlı). http://jsfiddle.net/WSzec/6/ Bu (şimdiki) uygulamada, anahtar isimleri için kurallar basittir, anahtarlar bir tamsayı ile başlayamaz veya nokta içeremez.

Misal:

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

DÜZENLEME 3 @AaditMShah'ın satır içi yol ayrıştırma yaklaşımı (String.split yerine) eklenmesi, düzensiz performansın iyileştirilmesine yardımcı oldu. Ulaşılan genel performans iyileştirmesinden çok memnunum.

En son jsfiddle ve jsperf:

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

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


7
"JSON nesnesi" diye bir şey yoktur . Soru JS nesneleriyle ilgili gibi görünüyor.
Felix Kling

1
Bu soru Code Review StackExchange
Aadit M Shah

6
@FelixKling - JSON nesnesi ile sadece ilkel JavaScript türlerini içeren JS nesneleri demek istedim. Örneğin, JS nesnesine bir işlev koyabilirsiniz, ancak bu JSON'a serileştirilmez - yani JSON.stringify ({fn: function () {alert ('a');}}); -
Louis Ricci

2
[1].[1].[0]bana yanlış geliyor. Bunun istenen sonuç olduğundan emin misiniz?
Bergi

2
Maalesef bir hata var: Tarih nesneleri boş bir JSON'a dönüştürülüyor.
giacecco

Yanıtlar:


217

İşte benim daha kısa uygulama:

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çok fazla değişmedi (ve bu isEmptydurumlara gerçekten ihtiyacınız olup olmadığından emin değilim ):

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

Birlikte, kıyas ölçünüzü yaklaşık yarısında çalıştırırlar (Opera 12.16: ~ 1900ms yerine ~ 900ms, Chrome 29: ~ 1600ms yerine ~ 800ms).

Not: Burada yanıtlanan bu ve diğer birçok çözüm hıza odaklanır ve prototip kirliliğine duyarlıdır ve güvenilir olmayan nesnelerde kullanılmamalıdır.


1
Bu harika! Regex (özellikle Chrome'da) oldukça iyi çalışıyor, bunu indexOf mantığı ile değiştirmeyi denedim, ancak sadece FF'de bir hızlanma gerçekleştirebildim. Başka bir zeki iyileştirmenin karıştırılıp karıştırılamayacağını görmek için bu soruya bir ödül ekleyeceğim, ancak şimdiye kadar bu umduğumdan daha fazlası.
Louis Ricci

1
Regex.exec () öğesini string.split () ile değiştirerek ve anahtar biçimini basitleştirerek uygulamanızdan daha fazla hız almayı başardım. Size puanları vermeden birkaç gün önce vereceğim, ama bence 'anlamlı optimizasyon duvarına' ulaşıldı.
Louis Ricci

JSON.flatten ({}); // {'': {}} - var results = {} 'dan sonra bir satır ekleyebilirsiniz. - if (sonuç === veri) veri döndürürse;
Ivan

@Ivan: Ah, bu son durum için teşekkürler, anlamsal olarak boş nesneler için ekstra bir temsil olması gerekecekti. Ama hayır, result === dataişe yaramaz, asla özdeş olmazlar.
Bergi

@Bergi Evet haklısın. Object.keys (data) .length === 0 olsa çalışıyor
Ivan

26

Ben iki işlev yazdım flattenve unflattenJSON nesnesi.


Bir JSON nesnesini düzleştirin :

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

Performans :

  1. Opera'daki mevcut çözümden daha hızlı. Mevcut çözüm Opera'da% 26 daha yavaş.
  2. Firefox'taki mevcut çözümden daha hızlı. Mevcut çözüm Firefox'ta% 9 daha yavaştır.
  3. Chrome'daki mevcut çözümden daha hızlı. Mevcut çözüm Chrome'da% 29 daha yavaş.

JSON nesnesini düzleştirme :

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

Performans :

  1. Opera'daki mevcut çözümden daha hızlı. Mevcut çözüm Opera'da% 5 daha yavaştır.
  2. Firefox'taki mevcut çözümden daha yavaş. Çözümüm Firefox'ta% 26 daha yavaş.
  3. Chrome'daki mevcut çözümden daha yavaş. Çözümüm Chrome'da% 6 daha yavaş.

Bir JSON nesnesini düzleştirin ve düzleştirin :

Genel olarak benim çözümüm, mevcut çözümden eşit derecede iyi ya da daha iyi performans gösteriyor.

Performans :

  1. Opera'daki mevcut çözümden daha hızlı. Mevcut çözüm Opera'da% 21 daha yavaştır.
  2. Firefox'taki mevcut çözüm kadar hızlı.
  3. Firefox'taki mevcut çözümden daha hızlı. Mevcut çözüm Chrome'da% 20 daha yavaştır.

Çıktı biçimi :

Düzleştirilmiş bir nesne, nesne özellikleri için nokta gösterimini ve dizi indeksleri için köşeli gösterimi kullanır:

  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}

Bence bu biçim sadece nokta gösterimini kullanmaktan daha iyidir:

  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}

Avantajları :

  1. Bir nesneyi düzleştirmek mevcut çözümden daha hızlıdır.
  2. Bir nesnenin düzleştirilmesi ve düzleştirilmemesi, mevcut çözümden daha hızlı veya daha hızlıdır.
  3. Düzleştirilmiş nesneler, okunabilirlik için hem nokta gösterimini hem de braket gösterimini kullanır.

Dezavantajları :

  1. Bir nesnenin düzleştirilmemesi, çoğu durumda (ancak tümü değil) mevcut çözümden daha yavaştır.

Geçerli JSFiddle demosu çıktı olarak şu değerleri verdi:

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

Güncellenmiş JSFiddle demom , çıktı olarak şu değerleri verdi:

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

Bunun ne anlama geldiğinden emin değilim, bu yüzden jsPerf sonuçlarına sadık kalacağım. Sonuçta jsPerf bir performans kıyaslama yardımcı programıdır. JSFiddle değil.


Çok havalı. Array.isArray ve Object'i daha yakın bir kapsama almak için anonim işlevleri kullanarak düzleştirme stilini gerçekten seviyorum. Ben JSPerf testi için kullandığınız test nesnesi olsa çok basit olduğunu düşünüyorum. Büyük bir karmaşık iç içe veri parçasının gerçek bir örneğini taklit etmek için jsfiddle karşılaştırmamda "fillObj ({}, 4)" nesnesini oluşturdum.
Louis Ricci

Bana nesnenin kodunu göster, ben de onu karşılaştırmaya ekleyeceğim.
Aadit M Shah

2
@LastCoder Hmmm, mevcut uygulamanız çoğu tarayıcıda (özellikle Firefox) benimkinden daha hızlı görünüyor. İlginçtir ki benim Opera uygulamam daha hızlı ve Chrome'da o kadar da kötü değil. Böyle büyük bir veri setine sahip olmanın algoritmanın hızını belirlemek için ideal bir faktör olduğunu düşünmüyorum çünkü: 1) büyük veri setleri büyük miktarda bellek, sayfa değişimi vb. ve bu, CPU yoğun bir iş yapmak istiyorsanız JS'de kontrol edebileceğiniz bir şey değildir (yani tarayıcının merhametindesiniz), o zaman JS en iyi dil değildir. Bunun yerine C kullanmayı düşünün. C
Aadit M Shah

1
bu iyi bir nokta ve sentetik ile gerçek dünya kıyaslaması arasındaki farkı ortaya koyuyor. Mevcut optimize edilmiş JS'nin performansından memnunum, bu yüzden C'yi kullanmaya gerek yok
Louis Ricci

Bu uygulama aynı zamanda bir prototip kirliliği hata var, örneğinunflatten({"foo.__proto__.bar": 42})
Alex Brasetvik

12

3 ½ Yıllar sonra ...

Kendi projem için JSON nesnelerini mongoDB nokta notasyonunda düzleştirmek istedim ve basit bir çözüm buldum :

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

Özellikler ve / veya uyarılar

  • Yalnızca JSON nesnelerini kabul eder. Eğer böyle bir {a: () => {}}şeyi geçerseniz, istediğinizi alamayabilirsiniz!
  • Boş dizileri ve nesneleri kaldırır. Yani bu {a: {}, b: []}düzleştirildi {}.

1
Güzel, ama kaçan alıntılarla ilgilenmiyorum. Böylece geçersiz {"x": "abc\"{x}\"yz"}olur { "x": "abc"{,"x",}"yz"}.
Simsteve7

@ Simsteve7 haklısın! Her zaman unutamayacağım bir şey!
Yan Foto

11

ES6 sürümü:

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

Misal:

console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));

1
JSON.stringify özellik adları (flatten ({"prop1": 0, "prop2": {"prop3": true, "prop4": "testi arasında ayırıcılar yoksa, unflattening konusunda bazı zorluklarınız olduğunu düşünüyorum. "}})); ==> {"prop1": 0, "prop2prop3": true, "prop2prop4": "test"} ama kolay bir düzeltme, ES6 sözdiziminin kısalığı gerçekten güzel
Louis Ricci

Bu çok doğru, ayırıcılar eklendi
Guy

Bu hoş bir şekilde oynamıyor Date, bunu nasıl yapacağını bilmiyor musunuz? Örneğin,flatten({a: {b: new Date()}});
Ehtesh Choudhury ile

Timestamps: {b: new Date (). GetTime ()}} kullanabilir ve daha sonra yeni Date (timestamp) ile adama döndürebilirsiniz
Guy

6

Yukarıdaki yanıttan daha yavaş çalışan (yaklaşık 1000 ms), ancak ilginç bir fikri olan başka bir yaklaşım :-)

Her özellik zinciri boyunca yineleme yapmak yerine, yalnızca son özelliği seçer ve geri kalanı için ara sonuçları saklamak için bir arama tablosu kullanır. Bu arama tablosu, hiçbir özellik zinciri kalmayıncaya ve tüm değerler eşlenmemiş özelliklerde bulunana kadar yinelenecektir.

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

Şu anda datatablo için giriş parametresini kullanıyor ve üzerine birçok özellik koyuyor - yıkıcı olmayan bir sürüm de mümkün olmalı. Belki akıllı bir lastIndexOfkullanım normal ifadeden daha iyi performans gösterir (normal ifadenin motoruna bağlıdır).

Burada çalışırken görün .


Cevabını küçümsemedim. Ancak, işlevinizin unflattendüzleştirilmiş nesneyi doğru bir şekilde yapmadığını belirtmek isterim . Örneğin diziyi düşünün [1,[2,[3,4],5],6]. Sizin flattenişlevi için bu nesneyi düzleştirir {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}. Sizin unflattenişlevi ancak yanlış için düzleştirilmiş nesneyi unflattens [1,[null,[3,4]],6]. Böyle nedeni ifadenin olduğu delete data[p]zamanından önce orta değerini siler [2,null,5]önce [3,4]buna eklenir. Çözmek için bir yığın kullanın. :-)
Aadit M Shah

1
Ah, görüyorum, tanımlanmamış numaralandırma sırası… Bunu bir özellik sırası ile düzeltirim, lütfen yığın çözümünüzü kendi cevabınıza koyun. İpucu için teşekkürler!
Bergi

4

Https://github.com/hughsk/flat kullanabilirsiniz

Yuvalanmış bir Javascript nesnesini alın ve düzleştirin veya bir nesneyi sınırlandırılmış tuşlarla düzleştirin.

Dokümandan örnek

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
Bunu AngularJS'de nasıl kullanıyorsunuz?
kensplanet

2

Bu kod, JSON nesnelerini yinelemeli olarak düzleştirir.

Zamanlama mekanizmamı koda ekledim ve bana 1 ms veriyor ama bunun en doğru olup olmadığından emin değilim.

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

Çıktı:

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

1
Bence, bu typeof some === 'object'daha hızlıdır , some instanceof Objectçünkü ilk kontrol O1'de ikinci iken On'da n burada bir kalıtım zincirinin uzunluğudur (Object her zaman orada sonuncusu olacaktır).
GullerYA

1

Küçük kod yeniden düzenleme ve özyinelemeli işlevi işlev ad alanının dışına taşıyarak seçilen cevaba +/-% 10-15 verimlilik ekledim.

Soruma bakın: Her boşlukta ad boşluklu işlevler yeniden değerlendiriliyor mu? bunun nedeni iç içe geçmiş işlevleri yavaşlatır.

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

Kıyaslama bölümüne bakınız .


1

Benimki burada. Büyük bir nesne üzerinde Google Apps Script'te <2ms olarak çalışır. Ayırıcılar için noktalar yerine tire kullanır ve sorucunun sorusundaki gibi özel olarak dizileri işlemez, ancak bu benim kullanımım için istediğim şeydi.

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

Misal:

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

Örnek çıktı:

[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

1

Bu kütüphaneyi kullanın:

npm install flat

Kullanım ( https://www.npmjs.com/package/flat adresinden ):

Düzleştirmek:

    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-düzleştirmek:

var unflatten = require('flat').unflatten

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

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }

2
Cevabınızı tamamlamak için bu kütüphanenin nasıl kullanılacağına dair bir örnek eklemelisiniz.
António Almeida

0

Ben, yukarıdaki jsFiddler ile problarıma göre, şu anda seçili olandan biraz daha hızlı olan yassılaştırılmış durumun yeni bir versiyonunu eklemek istiyorum (bu ihtiyacım olan şey :)). Dahası, kişisel olarak bu snippet'i biraz daha okunabilir görüyorum, bu da elbette çoklu geliştirici projeleri için önemlidir.

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

0

İşte birlikte çalıştığım bir nesneyi düzleştirmek için yazdığım bazı kod. Her yuvalanmış alanı alan ve onu ilk katmana getiren yeni bir sınıf oluşturur. Anahtarların orijinal yerleşimini hatırlayarak düzensiz olarak değiştirebilirsiniz. Ayrıca, anahtarların iç içe nesneler arasında bile benzersiz olduğunu varsayar. Umarım yardımcı olur.

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

Örnek olarak,

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

içine

{ a: 0, b: 1, c: 2 }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.