Cách nhanh nhất để làm phẳng / không làm phẳng các đối tượng JSON lồng nhau


159

Tôi đã ném một số mã với nhau để làm phẳng và hủy làm phẳng các đối tượng JSON phức tạp / lồng nhau. Nó hoạt động, nhưng hơi chậm (kích hoạt cảnh báo 'tập lệnh dài').

Đối với các tên phẳng tôi muốn "." làm dấu phân cách và [INDEX] cho mảng.

Ví dụ:

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}

Tôi đã tạo một điểm chuẩn ~ mô phỏng trường hợp sử dụng của mình http://jsfiddle.net/WSzec/

  • Nhận một đối tượng JSON lồng nhau
  • Làm phẳng nó
  • Nhìn qua nó và có thể sửa đổi nó trong khi làm phẳng
  • Hủy kết nối nó trở lại định dạng lồng nhau ban đầu của nó sẽ được chuyển đi

Tôi muốn mã nhanh hơn: Để làm rõ, mã hoàn thành điểm chuẩn JSFiddle ( http://jsfiddle.net/WSzec/ ) nhanh hơn đáng kể (~ 20% + sẽ tốt hơn) trong IE 9+, FF 24+ và Chrome 29 +.

Đây là mã JavaScript có liên quan: Nhanh nhất hiện tại: 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 Sửa đổi cách thực hiện ở trên thành triển khai của @Bergi hiện đang nhanh nhất. Bên cạnh đó, sử dụng ".indexOf" thay vì "regex.exec" nhanh hơn khoảng 20% ​​trong FF nhưng chậm hơn 20% trong Chrome; vì vậy tôi sẽ gắn bó với regex vì nó đơn giản hơn (đây là nỗ lực của tôi trong việc sử dụng indexOf để thay thế regex http://jsfiddle.net/WSzec/2/ ).

EDIT 2 Dựa trên ý tưởng của @Bergi, tôi đã tạo ra một phiên bản phi regex nhanh hơn (nhanh hơn gấp 3 lần trong FF và nhanh hơn 10% trong Chrome). http://jsfiddle.net/WSzec/6/ Trong triển khai (hiện tại) này, các quy tắc cho tên khóa chỉ đơn giản, các khóa không thể bắt đầu bằng một số nguyên hoặc chứa một dấu chấm.

Thí dụ:

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

EDIT 3 Thêm cách tiếp cận phân tích cú pháp đường dẫn nội tuyến của @AaditMShah (chứ không phải String.split) đã giúp cải thiện hiệu suất không kết hợp. Tôi rất hài lòng với sự cải thiện hiệu suất tổng thể đạt được.

Jsfiddle và jsperf mới nhất:

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

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


7
Không có thứ gọi là "đối tượng JSON" . Câu hỏi dường như là về các đối tượng JS.
Felix Kling

1
Câu hỏi này có vẻ phù hợp hơn với trang web StackExchange của Code Review: codereview.stackexchange.com
Aadit M Shah

6
@FelixKling - Theo đối tượng JSON, ý tôi là các đối tượng JS chỉ chứa các loại JavaScript nguyên thủy. Ví dụ, bạn có thể đặt một hàm trong một đối tượng JS, nhưng nó sẽ không được tuần tự hóa thành JSON - tức là JSON.opesify ({fn: function () {alert ('a');}}); -
Louis Ricci

2
[1].[1].[0]có vẻ sai với tôi Bạn có chắc chắn đây là kết quả mong muốn?
Bergi

2
Không may có một lỗi: Các đối tượng ngày được chuyển đổi thành một JSON trống.
giacecco

Câu trả lời:


217

Đây là cách thực hiện ngắn hơn nhiều của tôi:

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đã không thay đổi nhiều (và tôi không chắc liệu bạn có thực sự cần những isEmptytrường hợp đó không):

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

Cùng nhau, họ chạy điểm chuẩn của bạn trong khoảng một nửa thời gian (Opera 12.16: ~ 900ms thay vì ~ 1900ms, Chrome 29: ~ 800ms thay vì ~ 1600ms).

Lưu ý: Điều này và hầu hết các giải pháp khác được trả lời ở đây tập trung vào tốc độ và dễ bị ô nhiễm nguyên mẫu và không được sử dụng trên các vật thể không tin cậy.


1
Điều đó thật tuyệt! Regex chạy rất tốt (đặc biệt là trong Chrome), tôi đã thử thay thế nó bằng logic indexOf, nhưng chỉ có thể nhận ra sự tăng tốc trong FF. Tôi sẽ thêm một tiền thưởng cho câu hỏi này để xem liệu một cải tiến thông minh khác có thể được khuấy động hay không, nhưng cho đến nay điều này còn hơn cả những gì tôi đã hy vọng.
Louis Ricci

1
Tôi đã quản lý để giảm tốc độ thực hiện của bạn bằng cách thay thế regex.exec () bằng chuỗi.split () và đơn giản hóa định dạng khóa. Tôi sẽ cho nó vài ngày trước khi tôi trao giải cho bạn, nhưng tôi nghĩ rằng "bức tường tối ưu hóa có ý nghĩa" đã đạt được.
Louis Ricci

JSON.flatten ({}); // {'': {}} - bạn có thể thêm một dòng sau var result = {}; - if (result === data) dữ liệu trả về;
Ivan

@Ivan: Ah, cảm ơn vì trường hợp cạnh đó, mặc dù về mặt ngữ nghĩa, nó thực sự sẽ được yêu cầu có thêm một đại diện cho các đối tượng trống. Nhưng không, result === datasẽ không hoạt động, chúng không bao giờ giống nhau.
Bergi

@Bergi Vâng bạn nói đúng. Object.keys (dữ liệu) .length === 0 hoạt động mặc dù
Ivan

26

Tôi đã viết hai hàm flattenunflattenmột đối tượng JSON.


Làm phẳng một đối tượng 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));

Hiệu suất :

  1. Nó nhanh hơn giải pháp hiện tại trong Opera. Giải pháp hiện tại chậm hơn 26% trong Opera.
  2. Nó nhanh hơn giải pháp hiện tại trong Firefox. Giải pháp hiện tại chậm hơn 9% trong Firefox.
  3. Nó nhanh hơn giải pháp hiện tại trong Chrome. Giải pháp hiện tại chậm hơn 29% trong Chrome.

Hủy kết nối một đối tượng 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[""];
}

Hiệu suất :

  1. Nó nhanh hơn giải pháp hiện tại trong Opera. Giải pháp hiện tại chậm hơn 5% trong Opera.
  2. Nó chậm hơn giải pháp hiện tại trong Firefox. Giải pháp của tôi chậm hơn 26% trong Firefox.
  3. Nó chậm hơn giải pháp hiện tại trong Chrome. Giải pháp của tôi chậm hơn 6% trong Chrome.

Làm phẳng và hủy kết nối một đối tượng JSON :

Nhìn chung, giải pháp của tôi thực hiện tốt như nhau hoặc thậm chí tốt hơn giải pháp hiện tại.

Hiệu suất :

  1. Nó nhanh hơn giải pháp hiện tại trong Opera. Giải pháp hiện tại chậm hơn 21% trong Opera.
  2. Nó nhanh như giải pháp hiện tại trong Firefox.
  3. Nó nhanh hơn giải pháp hiện tại trong Firefox. Giải pháp hiện tại chậm hơn 20% trong Chrome.

Định dạng đầu ra :

Một đối tượng dẹt sử dụng ký hiệu chấm cho các thuộc tính đối tượng và ký hiệu ngoặc cho các chỉ số mảng:

  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}

Theo tôi định dạng này tốt hơn là chỉ sử dụng ký hiệu dấu chấm:

  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}

Ưu điểm :

  1. Làm phẳng một đối tượng nhanh hơn giải pháp hiện tại.
  2. Làm phẳng và làm phẳng một đối tượng nhanh bằng hoặc nhanh hơn giải pháp hiện tại.
  3. Các đối tượng dẹt sử dụng cả ký hiệu dấu chấm và ký hiệu dấu ngoặc để dễ đọc.

Nhược điểm :

  1. Hủy kết nối một đối tượng chậm hơn so với giải pháp hiện tại trong hầu hết các trường hợp (nhưng không phải tất cả).

Bản demo JSFiddle hiện tại đã đưa ra các giá trị sau làm đầu ra:

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

Bản demo JSFiddle được cập nhật của tôi đã đưa ra các giá trị sau làm đầu ra:

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

Tôi không thực sự chắc chắn điều đó có nghĩa là gì, vì vậy tôi sẽ gắn bó với kết quả của jsPerf. Sau khi tất cả jsPerf là ​​một tiện ích điểm chuẩn hiệu suất. JSFiddle thì không.


Rất tuyệt. Tôi thực sự thích phong cách làm phẳng, sử dụng các hàm ẩn danh để đưa Array.isArray và Object vào một phạm vi gần hơn. Tôi nghĩ rằng đối tượng thử nghiệm mà bạn sử dụng cho thử nghiệm JSPerf là ​​quá đơn giản. Tôi đã tạo đối tượng "fillObj ({}, 4)" trong điểm chuẩn jsfiddle của mình để mô phỏng trường hợp thực tế của một đoạn dữ liệu lồng nhau phức tạp lớn.
Louis Ricci

Chỉ cho tôi mã cho đối tượng của bạn và tôi sẽ kết hợp nó vào điểm chuẩn.
Aadit M Shah

2
@LastCoder Hmmm, việc triển khai hiện tại của bạn dường như nhanh hơn của tôi trong hầu hết các trình duyệt (đặc biệt là Firefox). Điều thú vị là việc triển khai của tôi nhanh hơn trong Opera và nó cũng không quá tệ trong Chrome. Tôi không nghĩ rằng có một tập dữ liệu lớn như vậy là một yếu tố lý tưởng để xác định tốc độ của thuật toán bởi vì: 1) các tập dữ liệu lớn cần một lượng lớn bộ nhớ, hoán đổi trang, v.v.; và đó không phải là thứ bạn có thể kiểm soát trong JS (tức là bạn đang bị thương bởi trình duyệt) 2) nếu bạn muốn làm công việc nặng về CPU thì JS không phải là ngôn ngữ tốt nhất. Cân nhắc sử dụng C thay thế. Có thư viện JSON cho C
Aadit M Shah

1
đó là một điểm tốt và mang đến sự khác biệt giữa điểm chuẩn tổng hợp so với thế giới thực. Tôi hài lòng với hiệu suất của JS được tối ưu hóa hiện tại, vì vậy không cần sử dụng C.
Louis Ricci

Việc triển khai này cũng có một lỗi ô nhiễm nguyên mẫu, ví dụunflatten({"foo.__proto__.bar": 42})
Alex Brasetvik

12

3 Năm sau ...

Đối với dự án của riêng tôi, tôi muốn làm phẳng các đối tượng JSON trong ký hiệu dấu chấm mongoDB và đưa ra một giải pháp đơn giản:

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

Các tính năng và / hoặc hãy cẩn thận

  • Nó chỉ chấp nhận các đối tượng JSON. Vì vậy, nếu bạn vượt qua một cái gì đó như {a: () => {}}bạn có thể không có được những gì bạn muốn!
  • Nó loại bỏ các mảng và đối tượng trống. Vì vậy, điều này {a: {}, b: []}được làm phẳng để {}.

1
Đẹp, nhưng tôi không quan tâm đến báo giá thoát. Vì vậy, {"x": "abc\"{x}\"yz"}trở thành { "x": "abc"{,"x",}"yz"}đó là unvalid.
Simsteve7

@ Simsteve7 bạn nói đúng! Một cái gì đó mà tôi luôn có xu hướng quên!
Yan Foto

11

Phiên bản 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 + '.')};
    }, {});
}

Thí dụ:

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

1
Tôi nghĩ bạn sẽ gặp khó khăn khi hủy kết nối nếu bạn không có dấu phân cách giữa các tên thuộc tính JSON.opesify (flatten ({"prop1": 0, "prop2": {"prop3": true, "prop4": "test "}})); ==> {"prop1": 0, "prop2prop3": true, "prop2prop4": "test"} nhưng là một sửa chữa dễ dàng, sự ngắn gọn của cú pháp ES6 thực sự rất hay
Louis Ricci

Điều đó rất đúng, các dấu phân cách được thêm vào
Guy

Điều này không chơi độc đáo với Date, bất kỳ ý tưởng làm thế nào để làm cho nó để làm điều đó? Ví dụ: vớiflatten({a: {b: new Date()}});
Ehtesh Choudhury

Bạn có thể sử dụng dấu thời gian: {b: Ngày mới (). GetTime ()}} và sau đó trả lại cho đến ngày với Ngày mới (dấu thời gian)
Guy

6

Đây là một cách tiếp cận khác chạy chậm hơn (khoảng 1000ms) so với câu trả lời ở trên, nhưng có một ý tưởng thú vị :-)

Thay vì lặp qua từng chuỗi thuộc tính, nó chỉ chọn thuộc tính cuối cùng và sử dụng bảng tra cứu cho phần còn lại để lưu trữ các kết quả trung gian. Bảng tra cứu này sẽ được lặp đi lặp lại cho đến khi không còn chuỗi thuộc tính nào và tất cả các giá trị nằm trên các thuộc tính không tương thích.

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

Hiện tại nó sử dụng datatham số đầu vào cho bảng và đặt rất nhiều thuộc tính trên đó - một phiên bản không phá hủy cũng có thể. Có thể một lastIndexOfcách sử dụng thông minh thực hiện tốt hơn regex (phụ thuộc vào công cụ regex).

Xem nó trong hành động ở đây .


Tôi không đánh giá thấp câu trả lời của bạn. Tuy nhiên tôi muốn chỉ ra rằng chức năng của bạn không unflattenđúng đối tượng làm phẳng. Ví dụ xem xét các mảng [1,[2,[3,4],5],6]. flattenChức năng của bạn làm phẳng đối tượng này để {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}. unflattenTuy nhiên, chức năng của bạn không chính xác làm phẳng đối tượng làm phẳng thành [1,[null,[3,4]],6]. Lý do điều này xảy ra là do câu lệnh delete data[p]xóa sớm giá trị trung gian [2,null,5]trước khi [3,4]được thêm vào nó. Sử dụng một ngăn xếp để giải quyết nó. :-)
Aadit M Shah

1
À, tôi hiểu rồi, thứ tự liệt kê không xác định được Gon Gonna sửa nó với một hàng thuộc tính, vui lòng đặt giải pháp ngăn xếp của bạn vào một câu trả lời riêng. Cảm ơn các gợi ý!
Bergi

4

Bạn có thể sử dụng https://github.com/hughsk/flat

Lấy một đối tượng Javascript lồng nhau và làm phẳng nó hoặc hủy kết nối một đối tượng với các phím được phân tách.

Ví dụ từ tài liệu

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
Làm thế nào để bạn sử dụng điều này trong AngularJS?
kensplanet

2

Mã này đệ quy làm phẳng các đối tượng JSON.

Tôi đã bao gồm cơ chế thời gian của mình trong mã và nó mang lại cho tôi 1ms nhưng tôi không chắc đó có phải là cơ chế chính xác nhất không.

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

Đầu ra:

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
Tôi nghĩ, điều đó typeof some === 'object'nhanh hơn some instanceof Objectvì lần kiểm tra đầu tiên thực hiện trong O1 trong khi lần thứ hai trong On trong đó n là độ dài của chuỗi thừa kế (Object sẽ luôn là lần cuối cùng ở đó).
GullerYA

1

Tôi đã thêm hiệu quả +/- 10-15% cho câu trả lời được chọn bằng cách tái cấu trúc mã nhỏ và di chuyển hàm đệ quy bên ngoài không gian tên hàm.

Xem câu hỏi của tôi: Các hàm được đặt tên được đánh giá lại trên mỗi cuộc gọi? tại sao điều này làm chậm các chức năng lồng nhau xuống.

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

Xem điểm chuẩn .


1

Đây là của tôi. Nó chạy trong <2ms trong Google Apps Script trên một đối tượng khá lớn. Nó sử dụng dấu gạch ngang thay vì dấu chấm cho dấu phân cách và nó không xử lý các mảng đặc biệt như trong câu hỏi của người hỏi, nhưng đây là những gì tôi muốn cho việc sử dụng của mình.

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

Thí dụ:

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

Ví dụ đầu ra:

[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

Sử dụng thư viện này:

npm install flat

Cách sử dụng (từ https://www.npmjs.com/package/flat ):

Làm phẳng:

    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-flatten:

var unflatten = require('flat').unflatten

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

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

2
Để hoàn thành câu trả lời của bạn, bạn nên thêm một ví dụ về cách sử dụng thư viện đó.
António Almeida

0

Tôi muốn thêm một phiên bản mới của trường hợp làm phẳng (đây là những gì tôi cần :)), theo thăm dò của tôi với jsFiddler ở trên, nhanh hơn một chút so với trường hợp hiện đang được chọn. Hơn nữa, cá nhân tôi thấy đoạn trích này dễ đọc hơn một chút, điều này tất nhiên rất quan trọng đối với các dự án đa nhà phát triển.

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

Đây là một số mã tôi đã viết để làm phẳng một đối tượng mà tôi đang làm việc. Nó tạo ra một lớp mới lấy mọi trường lồng nhau và đưa nó vào lớp đầu tiên. Bạn có thể sửa đổi nó thành unattatten bằng cách nhớ vị trí ban đầu của các phím. Nó cũng giả sử các khóa là duy nhất ngay cả trên các đối tượng lồng nhau. Hy vọng nó giúp.

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

Ví dụ, nó chuyển đổi

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

vào

{ a: 0, b: 1, c: 2 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.