sắp xếp thuộc tính đối tượng và JSON.stringify


94

Ứng dụng của tôi có một mảng lớn các đối tượng, tôi xâu chuỗi và lưu chúng vào đĩa. Thật không may, khi các đối tượng trong mảng được thao tác và đôi khi được thay thế, các thuộc tính trên các đối tượng được liệt kê theo các thứ tự khác nhau (thứ tự tạo của chúng?). Khi tôi thực hiện JSON.stringify () trên mảng và lưu nó, một điểm khác biệt hiển thị các thuộc tính được liệt kê theo các thứ tự khác nhau, điều này gây khó chịu khi cố gắng hợp nhất dữ liệu hơn nữa bằng các công cụ khác biệt và hợp nhất.

Lý tưởng nhất là tôi muốn sắp xếp các thuộc tính của các đối tượng theo thứ tự bảng chữ cái trước khi thực hiện chuỗi ký tự hoặc như một phần của hoạt động chuỗi ký tự. Có mã để thao tác các đối tượng mảng ở nhiều nơi và việc thay đổi chúng để luôn tạo thuộc tính theo một thứ tự rõ ràng sẽ rất khó.

Đề xuất sẽ được hoan nghênh nhất!

Một ví dụ ngắn gọn:

obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);

Đầu ra của hai lệnh gọi stringify này khác nhau và hiển thị trong một dữ liệu khác nhau của tôi, nhưng ứng dụng của tôi không quan tâm đến thứ tự của các thuộc tính. Các đối tượng được xây dựng theo nhiều cách và nhiều nơi.


Xin vui lòng cho một ví dụ về đối tượng bạn đang cố gắng để stringify (hoặc đầu ra JSON hoặc đối tượng đen JS)
Bojangles

4
Các khóa đối tượng trong các đối tượng không được đảm bảo có thứ tự cố định. Đây là do thiết kế.
Người qua đường


1
@rab, bạn tìm ra cái đó ở đâu vậy? Lưu ý: Nó có thể hoạt động (và có lẽ là thường xuyên), nhưng ý tôi là nó không được đảm bảo.
Dogbert

1
OP tại đây. Không có sự phụ thuộc vào thứ tự thuộc tính ở đây, chỉ là một câu hỏi về cách tránh sự khác biệt trong dữ liệu được tuần tự hóa. Điều này cuối cùng đã được giải quyết bằng cách lưu trữ các thuộc tính trong các mảng và sắp xếp chúng trước khi tuần tự hóa, giống như trong câu trả lời được chấp nhận bên dưới.
Innovine

Câu trả lời:


73

Phương pháp đơn giản hơn, hiện đại và hiện được hỗ trợ bởi trình duyệt chỉ đơn giản là:

JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());

Tuy nhiên, phương pháp này loại bỏ bất kỳ đối tượng lồng nhau nào không được tham chiếu và không áp dụng cho các đối tượng trong mảng. Bạn cũng sẽ muốn làm phẳng đối tượng sắp xếp nếu bạn muốn một cái gì đó giống như đầu ra này:

{"a":{"h":4,"z":3},"b":2,"c":1}

Bạn có thể làm điều đó với điều này:

var flattenObject = function(ob) {
    var toReturn = {};

    for (var i in ob) {
        if (!ob.hasOwnProperty(i)) continue;

        if ((typeof ob[i]) == 'object') {
            var flatObject = flattenObject(ob[i]);
            for (var x in flatObject) {
                if (!flatObject.hasOwnProperty(x)) continue;

                toReturn[i + '.' + x] = flatObject[x];
            }
        } else {
            toReturn[i] = ob[i];
        }
    }
    return toReturn;
};

JSON.stringify(sortMyObj, Object.keys(flattenObject(sortMyObj)).sort());

Để làm điều đó theo chương trình với thứ gì đó bạn có thể tự tinh chỉnh, bạn cần đẩy tên thuộc tính đối tượng vào một mảng, sau đó sắp xếp mảng theo thứ tự bảng chữ cái và lặp qua mảng đó (sẽ theo đúng thứ tự) và chọn từng giá trị từ đối tượng trong đơn đặt hàng đó. "hasOwnProperty" cũng được chọn, vì vậy bạn chắc chắn chỉ có các thuộc tính riêng của đối tượng. Đây là một ví dụ:

var obj = {"a":1,"b":2,"c":3};

function iterateObjectAlphabetically(obj, callback) {
    var arr = [],
        i;

    for (i in obj) {
        if (obj.hasOwnProperty(i)) {
            arr.push(i);
        }
    }

    arr.sort();

    for (i = 0; i < arr.length; i++) {
        var key = obj[arr[i]];
        //console.log( obj[arr[i]] ); //here is the sorted value
        //do what you want with the object property
        if (callback) {
            // callback returns arguments for value, key and original object
            callback(obj[arr[i]], arr[i], obj);
        }
    }
}

iterateObjectAlphabetically(obj, function(val, key, obj) {
    //do something here
});

Một lần nữa, điều này sẽ đảm bảo rằng bạn lặp lại theo thứ tự bảng chữ cái.

Cuối cùng, hãy hiểu thêm về cách đơn giản nhất, thư viện này sẽ cho phép bạn sắp xếp một cách đệ quy bất kỳ JSON nào mà bạn chuyển vào nó: https://www.npmjs.com/package/json-stable-stringify

var stringify = require('json-stable-stringify');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));

Đầu ra

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}

9
Đây là rất nhiều điều tôi đã cố gắng tránh :) Nhưng cảm ơn, có vẻ như là giải pháp chính xác, nếu nặng,.
Innovine

1
Có một lỗi trong mã này. Bạn không thể tham chiếu obj[i]trong vòng lặp for vì ilà một số nguyên và tên thuộc tính không nhất thiết (do đó câu hỏi này). Nó nên được obj[arr[i]].
Andrew Ensley

1
Các lệnh gọi lại không dành riêng cho hành vi không đồng bộ.
markyzm

2
@Johann Bằng cách sử dụng một lệnh gọi lại ở đây, anh ta biến iterateObjectAlphabeticallythành một hàm có thể tái sử dụng. Nếu không có lệnh gọi lại, 'mã tải trọng' sẽ phải nằm bên trong chính hàm. Tôi nghĩ rằng đó là một giải pháp rất thanh lịch và được sử dụng trong nhiều thư viện và chính JS core. Ví dụ: Array.forEach .
Stijn de Witt

1
@marksyzm Không sao, trong trường hợp của tôi, tôi chỉ phải xâu chuỗi một số trường lựa chọn. Vì vậy, câu trả lời của bạn đưa tôi vào con đường. Cảm ơn
Benj

39

Tôi nghĩ rằng nếu bạn đang kiểm soát thế hệ JSON (và có vẻ như bạn đang có), thì đối với mục đích của bạn, đây có thể là một giải pháp tốt: json-stable-stringify

Từ trang web của dự án:

JSON.stringify () xác định với phân loại tùy chỉnh để nhận các băm xác định từ các kết quả được chuỗi

Nếu JSON được tạo ra là xác định, bạn sẽ có thể dễ dàng khác biệt / hợp nhất nó.


Lưu ý rằng repo đó đang ở trạng thái xấu: chưa được cập nhật trong 3 năm và có các vấn đề chưa được giải quyết và các yêu cầu kéo. Tôi nghĩ tốt hơn là nên xem xét một số câu trả lời mới hơn.
Tom

3
@Tom Cũng lưu ý rằng repo đó có 5 triệu lượt tải xuống hàng tuần (!). Nói cách khác, nó đang được sử dụng một cách tích cực. Có các vấn đề mở 'chưa được giải quyết' và / hoặc yêu cầu kéo không có nhiều ý nghĩa. Chỉ là (các) tác giả không quan tâm đến những vấn đề đó hoặc không có thời gian, v.v. Không phải mọi lib đều cần được cập nhật vài tháng một lần. Trên thực tế, imho, những người giỏi nhất nhận được cập nhật rất hiếm khi. Khi điều gì đó được hoàn thành, nó đã hoàn thành. Tôi không nói rằng dự án này không có vấn đề thực sự, nhưng nếu vậy, hãy chỉ ra những điều đó. Tôi không liên quan đến lib BTW này.
Stijn de Witt

ES6 Tôi đoán nó đã phá vỡ khả năng sắp xếp các đối tượng (không phải mảng), như được chỉ định. Tôi cần cái này để chỉnh sửa người dùng. Ngay cả khi thứ tự không quan trọng đối với máy tính. Người dùng chỉnh sửa nó. Nó đáng để thử.
TamusJRoyce

Ngoài những gì @Tom nói, tôi cũng sẽ chỉ ra rằng dự án này không có phụ thuộc thời gian chạy. Có nghĩa là nếu không có vấn đề bảo mật / bảo trì trong repo này, thì nó có thể khá ổn định và an toàn để sử dụng. Ngoài ra, tôi vừa thử nghiệm nó với ES6 và nó có vẻ hoạt động tốt (ít nhất là trong Firefox).
Phil

27

Bạn có thể chuyển một mảng tên thuộc tính đã được sắp xếp làm đối số thứ hai của JSON.stringify():

JSON.stringify(obj, Object.keys(obj).sort())

1
Có bất kỳ loại đảm bảo bằng văn bản nào về hành vi này không?
rich remer

4
@richremer Có, trong tiêu chuẩn ECMAScript , tham số thứ hai của JSON.stringify()được gọi replacervà có thể là một mảng. Nó được sử dụng để xây dựng một PropertyListsau đó được sử dụng SerializeJSONObject()để nối các thuộc tính theo thứ tự đó. Đây là tài liệu từ ECMAScript 5.
Christian d'Heureuse

14
Lưu ý rằng nếu obj có các đối tượng lồng nhau trong đó, thì các khóa lồng nhau cũng phải có trong đối số thứ hai; nếu không, chúng sẽ bị loại bỏ! Counter-ví dụ: > JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' Một (hacky) cách để xây dựng danh sách của tất cả các phím có liên quan là sử dụng JSON.stringify đi qua các đối tượng: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } Ví dụ:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim

Object.keys () không phải là đệ quy. Điều này sẽ không hoạt động đối với bất kỳ đối tượng nào có chứa các mảng hoặc đối tượng lồng nhau.
Jor

25

Tôi không hiểu tại sao lại cần sự phức tạp của các câu trả lời tốt nhất hiện tại để lấy tất cả các khóa một cách đệ quy. Trừ khi cần đến hiệu suất hoàn hảo, đối với tôi, dường như chúng ta chỉ có thể gọi JSON.stringify()hai lần, lần đầu tiên để nhận tất cả các phím và lần thứ hai, để thực sự hoàn thành công việc. Bằng cách đó, tất cả độ phức tạp của đệ quy đều được xử lý stringifyvà chúng ta biết rằng nó biết nội dung của nó và cách xử lý từng loại đối tượng:

function JSONstringifyOrder( obj, space )
{
    var allKeys = [];
    JSON.stringify( obj, function( key, value ){ allKeys.push( key ); return value; } )
    allKeys.sort();
    return JSON.stringify( obj, allKeys, space );
}

điều đó thật thú vị, nhưng bạn đã thực sự "lừa dối" bằng cách sử dụng stringify để ánh xạ tất cả các khóa thành một mảng. Mảng đó có thể dễ dàng hơn, tốt hơn và an toàn hơn qua Object.keys
Z. Khullah.

5
Không, vấn đề Object.keys()không phải là đệ quy, vì vậy nếu bạn có một đối tượng giống như {a: "A", b: {c: "C"} }và bạn gọi Object.keys trên đó, bạn sẽ chỉ nhận được các khóa abnhưng không c. JSON.stringifybiết mọi thứ về đệ quy trên mọi loại đối tượng được xử lý bởi quá trình tuần tự hóa JSON, có thể là một đối tượng giống như một mảng (và nó đã triển khai logic để nhận ra cả hai), vì vậy nó sẽ xử lý mọi thứ một cách chính xác cho chúng ta.
Jor

1
Thích giải pháp này, rất thanh lịch và có vẻ như không an toàn.
Markus

14

Cập nhật 2018-7-24:

Phiên bản này sắp xếp các đối tượng lồng nhau và cũng hỗ trợ mảng:

function sortObjByKey(value) {
  return (typeof value === 'object') ?
    (Array.isArray(value) ?
      value.map(sortObjByKey) :
      Object.keys(value).sort().reduce(
        (o, key) => {
          const v = value[key];
          o[key] = sortObjByKey(v);
          return o;
        }, {})
    ) :
    value;
}


function orderedJsonStringify(obj) {
  return JSON.stringify(sortObjByKey(obj));
}

Trường hợp thử nghiệm:

  describe('orderedJsonStringify', () => {
    it('make properties in order', () => {
      const obj = {
        name: 'foo',
        arr: [
          { x: 1, y: 2 },
          { y: 4, x: 3 },
        ],
        value: { y: 2, x: 1, },
      };
      expect(orderedJsonStringify(obj))
        .to.equal('{"arr":[{"x":1,"y":2},{"x":3,"y":4}],"name":"foo","value":{"x":1,"y":2}}');
    });

    it('support array', () => {
      const obj = [
        { x: 1, y: 2 },
        { y: 4, x: 3 },
      ];
      expect(orderedJsonStringify(obj))
        .to.equal('[{"x":1,"y":2},{"x":3,"y":4}]');
    });

  });

Câu trả lời không được chấp nhận:

Một phiên bản ngắn gọn trong ES2016. Ghi có vào @codename, từ https://stackoverflow.com/a/29622653/94148

function orderedJsonStringify(o) {
  return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}

Cú pháp không đúng. Nên -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Ben

Điều này hiện đã khắc phục sự cố của tôi với C # DataContractJsonSerializer và "__type" không được liệt kê đầu tiên trong chuỗi json. Cảm ơn.
Yogurt The Wise

2
Lưu ý rằng điều này, giống như câu trả lời của Christian, hoạt động sai với các đối tượng lồng nhau.
Daniel Griscom

sortObjPropertiesByKey không được xác định. Ý của bạn là sử dụng sortObjByKey?
Paul Lynch

Có vẻ như điều này không sortObjByKey()kiểm tra các tham chiếu vòng tròn, vì vậy hãy cẩn thận, nó có thể bị treo trong một vòng lặp vô hạn tùy thuộc vào dữ liệu đầu vào. Ví dụ: var objA = {}; var objB = {objA: objA}; objA.objB = objB;-> sortObjByKey(objA);->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
Klesun


4

Điều này giống như câu trả lời của Satpal Singh

function stringifyJSON(obj){
    keys = [];
    if(obj){
        for(var key in obj){
            keys.push(key);
        }
    }
    keys.sort();
    var tObj = {};
    var key;
    for(var index in keys){
        key = keys[index];
        tObj[ key ] = obj[ key ];
    }
    return JSON.stringify(tObj);
}

obj1 = {}; obj1.os="linux"; obj1.name="X";
stringifyJSON(obj1); //returns "{"name":"X","os":"linux"}"

obj2 = {}; obj2.name="X"; obj2.os="linux";
stringifyJSON(obj2); //returns "{"name":"X","os":"linux"}"

3

Bạn có thể thêm một toJSONhàm tùy chỉnh vào đối tượng mà bạn có thể sử dụng để tùy chỉnh đầu ra. Bên trong hàm, việc thêm các thuộc tính hiện tại vào một đối tượng mới theo một thứ tự cụ thể sẽ bảo toàn thứ tự đó khi được chuỗi.

Xem tại đây:

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify

Không có phương pháp tích hợp nào để kiểm soát thứ tự vì dữ liệu JSON được sử dụng để truy cập bằng các khóa.

Đây là một jsfiddle với một ví dụ nhỏ:

http://jsfiddle.net/Eq2Yw/

Hãy thử nhận xét về toJSONchức năng - thứ tự của các thuộc tính bị đảo ngược. Xin lưu ý rằng điều này có thể dành riêng cho từng trình duyệt, tức là đặt hàng không được hỗ trợ chính thức trong đặc điểm kỹ thuật. Nó hoạt động trong phiên bản Firefox hiện tại, nhưng nếu bạn muốn có một giải pháp mạnh mẽ 100%, bạn có thể phải viết hàm stringifier của riêng mình.

Biên tập:

Cũng xem câu hỏi SO này liên quan đến đầu ra không xác định của stringify, đặc biệt là chi tiết của Daff về sự khác biệt của trình duyệt:

Làm cách nào để xác minh một cách xác định rằng một đối tượng JSON chưa được sửa đổi?


Các thuộc tính của mỗi đối tượng đã được biết đến (và hầu hết là các chuỗi) vì vậy việc mã hóa cứng các thuộc tính trong bộ định chuỗi toJSON của riêng tôi có thể nhanh hơn nhiều so với sắp xếp ...!
Innovine

'OrderJsonStringify' bên dưới gần như đã hoạt động. Nhưng điều này có vẻ là một giải pháp tốt hơn cho tôi. Khi tôi cần lấy '__type' của C # DataContractJsonSerializer làm mục đầu tiên trong chuỗi json. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' vv .....]); một chút đau đớn để có danh sách tất cả các phím.
Sữa chua The Wise

3

Một câu trả lời đệ quy và đơn giản hóa:

function sortObject(obj) {
    if(typeof obj !== 'object')
        return obj
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]]);       
    return temp;
}

var str = JSON.stringify(sortObject(obj), undefined, 4);

reordernên được đổi tên thành sortObjectvà điều này không xử lý mảng
Jonathan Gawrych

Cảm ơn bạn đã nhận thấy điều đó và vấn đề của OP là sắp xếp lại các đối tượng, không phải mảng.
Jason Parham

@JonathanGawrych Tại sao bạn lại sắp xếp các mảng, chúng phải có một thứ tự. Mảng [1,2,3] không giống với mảng [2,3,1], nhưng một đối tượng không có thứ tự thuộc tính. Vấn đề là thứ tự đột nhiên có vấn đề sau khi chuỗi - các chuỗi cho các đối tượng tương đương 100% có thể khác nhau. Đáng tiếc là những nỗ lực để có được một stringify xác định dường như đáng kể, ví dụ gói: github.com/substack/json-stable-stringify/blob/master/index.js
Morre

1
@ Mörre - Tôi đoán tôi không đủ cụ thể. Bởi "không xử lý mảng", ý tôi là các mảng bị hỏng, không phải là không được sắp xếp. Vấn đề là vì typeof arr === "object"nó sẽ biến đổi mảng thành đối tượng. Ví dụ var obj = {foo: ["bar", "baz"]}sẽ được chuyển đổi thành chuỗi thành{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych

để khắc phục sự cố được tìm thấy bởi if(obj.constructor.prototype !== Object.prototype)
@JonathanGawrych

3

Bạn có thể sắp xếp đối tượng theo tên thuộc tính trong EcmaScript 2015

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}

Có lẽ một cái tên tốt hơn sẽ là sortObjectPropertiesByName()hoặc đơn giản hơn sortPropertiesByName().
Ngày

3

Tôi đã lấy câu trả lời từ @Jason Parham và thực hiện một số cải tiến

function sortObject(obj, arraySorter) {
    if(typeof obj !== 'object')
        return obj
    if (Array.isArray(obj)) {
        if (arraySorter) {
            obj.sort(arraySorter);
        }
        for (var i = 0; i < obj.length; i++) {
            obj[i] = sortObject(obj[i], arraySorter);
        }
        return obj;
    }
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]], arraySorter);       
    return temp;
}

Điều này khắc phục sự cố mảng được chuyển đổi thành đối tượng và nó cũng cho phép bạn xác định cách sắp xếp mảng.

Thí dụ:

var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)

đầu ra:

{content:[{id:1},{id:2},{id:3}]}

2

Câu trả lời được chấp nhận không hoạt động đối với tôi cho các đối tượng lồng nhau vì một số lý do. Điều này khiến tôi phải viết mã của riêng mình. Khi tôi viết bài này vào cuối năm 2019, có một số tùy chọn khác có sẵn trong ngôn ngữ.

Cập nhật: Tôi tin rằng câu trả lời của David Furlong là một cách tiếp cận thích hợp hơn cho nỗ lực trước đó của tôi và tôi đã phản bác lại điều đó. Của tôi dựa trên sự hỗ trợ cho Object.entries (...), vì vậy không hỗ trợ Internet Explorer.

function normalize(sortingFunction) {
  return function(key, value) {
    if (typeof value === 'object' && !Array.isArray(value)) {
      return Object
        .entries(value)
        .sort(sortingFunction || undefined)
        .reduce((acc, entry) => {
          acc[entry[0]] = entry[1];
          return acc;
        }, {});
    }
    return value;
  }
}

JSON.stringify(obj, normalize(), 2);

-

GIỮ PHIÊN BẢN CŨ NÀY ĐỂ THAM KHẢO LỊCH SỬ

Tôi thấy rằng một mảng phẳng, đơn giản của tất cả các phím trong đối tượng sẽ hoạt động. Trong hầu hết các trình duyệt (có thể dự đoán được không phải Edge hay Internet explorer) và Node 12+, có một giải pháp khá ngắn gọn là Array.prototype.flatMap (...) hiện có sẵn. (Tương đương với lodash cũng sẽ hoạt động.) Tôi chỉ thử nghiệm trong Safari, Chrome và Firefox, nhưng không hiểu lý do gì mà nó không hoạt động ở bất kỳ nơi nào khác hỗ trợ flatMap và JSON.stringify (...) .

function flattenEntries([key, value]) {
  return (typeof value !== 'object')
    ? [ [ key, value ] ]
    : [ [ key, value ], ...Object.entries(value).flatMap(flattenEntries) ];
}

function sortedStringify(obj, sorter, indent = 2) {
  const allEntries = Object.entries(obj).flatMap(flattenEntries);
  const sorted = allEntries.sort(sorter || undefined).map(entry => entry[0]);
  return JSON.stringify(obj, sorted, indent);
}

Với điều này, bạn có thể xâu chuỗi mà không có sự phụ thuộc của bên thứ ba và thậm chí chuyển vào thuật toán sắp xếp của riêng bạn để sắp xếp các cặp mục nhập khóa-giá trị, vì vậy bạn có thể sắp xếp theo khóa, trọng tải hoặc kết hợp cả hai. Hoạt động cho các đối tượng, mảng lồng nhau và bất kỳ hỗn hợp nào của các kiểu dữ liệu cũ thuần túy.

const obj = {
  "c": {
    "z": 4,
    "x": 3,
    "y": [
      2048,
      1999,
      {
        "x": false,
        "g": "help",
        "f": 5
      }
    ]
  },
  "a": 2,
  "b": 1
};

console.log(sortedStringify(obj, null, 2));

Bản in:

{
  "a": 2,
  "b": 1,
  "c": {
    "x": 3,
    "y": [
      2048,
      1999,
      {
        "f": 5,
        "g": "help",
        "x": false
      }
    ],
    "z": 4
  }
}

Nếu bạn phải có khả năng tương thích với các công cụ JavaScript cũ hơn , bạn có thể sử dụng các phiên bản dài dòng hơn một chút để mô phỏng hành vi flatMap. Ứng dụng khách phải hỗ trợ ít nhất ES5, vì vậy không có Internet Explorer 8 trở xuống.

Chúng sẽ trả về kết quả tương tự như trên.

function flattenEntries([key, value]) {
  if (typeof value !== 'object') {
    return [ [ key, value ] ];
  }
  const nestedEntries = Object
    .entries(value)
    .map(flattenEntries)
    .reduce((acc, arr) => acc.concat(arr), []);
  nestedEntries.unshift([ key, value ]);
  return nestedEntries;
}

function sortedStringify(obj, sorter, indent = 2) {
  const sortedKeys = Object
    .entries(obj)
    .map(flattenEntries)
    .reduce((acc, arr) => acc.concat(arr), [])
    .sort(sorter || undefined)
    .map(entry => entry[0]);
  return JSON.stringify(obj, sortedKeys, indent);
}

1

Làm việc với lodash, các đối tượng lồng nhau, bất kỳ giá trị nào của thuộc tính đối tượng:

function sort(myObj) {
  var sortedObj = {};
  Object.keys(myObj).sort().forEach(key => {
    sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : myObj[key]
  })
  return sortedObj;
}
JSON.stringify(sort(yourObj), null, 2)

Nó dựa trên hành vi của Chrome và Node mà khóa đầu tiên được gán cho một đối tượng sẽ được xuất trước JSON.stringify.


2
Cảm ơn, nhưng tôi đã gặp vấn đề này 4 năm trước, tôi rất vui khi nói rằng tôi đã tiếp tục một chút kể từ đó :)
Innovine

1
:) Tốt để nghe nó. Mặc dù tôi đã gặp sự cố này ngày hôm qua và không tìm thấy câu trả lời mà tôi đang tìm kiếm trong câu hỏi (cũ nhưng vẫn có liên quan) của bạn :)
AJP

0

Thử:

function obj(){
  this.name = '';
  this.os = '';
}

a = new obj();
a.name = 'X',
a.os = 'linux';
JSON.stringify(a);
b = new obj();
b.os = 'linux';
b.name = 'X',
JSON.stringify(b);

Cảm ơn, nhưng rõ ràng thứ tự không được xác định và chỉ xảy ra để làm việc. Cách tiếp cận thú vị mặc dù!
Innovine

0

Tôi đã tạo một hàm để sắp xếp đối tượng và với lệnh gọi lại .. thực sự tạo ra một đối tượng mới

function sortObj( obj , callback ) {

    var r = [] ;

    for ( var i in obj ){
        if ( obj.hasOwnProperty( i ) ) {
             r.push( { key: i , value : obj[i] } );
        }
    }

    return r.sort( callback ).reduce( function( obj , n ){
        obj[ n.key ] = n.value ;
        return obj;
    },{});
}

và gọi nó bằng object.

var obj = {
    name : "anu",
    os : "windows",
    value : 'msio',
};

var result = sortObj( obj , function( a, b ){
    return a.key < b.key  ;    
});

JSON.stringify( result )

cái nào sẽ in {"value":"msio","os":"windows","name":"anu"}và để phân loại với giá trị.

var result = sortObj( obj , function( a, b ){
    return a.value < b.value  ;    
});

JSON.stringify( result )

cái nào in {"os":"windows","value":"msio","name":"anu"}


1
Hoạt động tốt, nhưng tiếc là nó không đệ quy.
Jonathan Gawrych

0

Nếu các đối tượng trong danh sách không có cùng thuộc tính, hãy tạo một đối tượng chính kết hợp trước khi xâu chuỗi:

let arr=[ <object1>, <object2>, ... ]
let o = {}
for ( let i = 0; i < arr.length; i++ ) {
  Object.assign( o, arr[i] );
}
JSON.stringify( arr, Object.keys( o ).sort() );


0
function FlatternInSort( obj ) {
    if( typeof obj === 'object' )
    {
        if( obj.constructor === Object )
        {       //here use underscore.js
            let PaireStr = _( obj ).chain().pairs().sortBy( p => p[0] ).map( p => p.map( FlatternInSort ).join( ':' )).value().join( ',' );
            return '{' + PaireStr + '}';
        }
        return '[' + obj.map( FlatternInSort ).join( ',' ) + ']';
    }
    return JSON.stringify( obj );
}

// ví dụ như bên dưới. trong mỗi lớp, đối với các đối tượng như {}, được làm phẳng theo thứ tự chính. cho mảng, số hoặc chuỗi, được làm phẳng giống như / với JSON.stringify.

FlatternInSort ({c: 9, b: {y: 4, z: 2, e: 9}, F: 4, a: [{j: 8, h: 3}, {a: 3, b: 7}] })

"{" F ": 4," a ": [{" h ": 3," j ": 8}, {" a ": 3," b ": 7}]," b ": {" e " : 9, "y": 4, "z": 2}, "c": 9} "


Vui lòng giải thích lý do tại sao sử dụng lib bên ngoài và như @DeKaNszn đã nói, hãy thêm một số giải thích và phần giới thiệu. Nó sẽ được đánh giá cao.
Benj

funtion này được sao chép từ dự án của tôi. trong đó tôi sử dụng underscore.js.
thánh

0

Mở rộng câu trả lời của AJP, để xử lý các mảng:

function sort(myObj) {
    var sortedObj = {};
    Object.keys(myObj).sort().forEach(key => {
        sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : _.isArray(myObj[key])? myObj[key].map(sort) : myObj[key]
    })
    return sortedObj;
}

0

Ngạc nhiên là không ai đề cập đến isEqualchức năng của lodash .

Thực hiện so sánh sâu giữa hai giá trị để xác định xem chúng có tương đương hay không.

Lưu ý: Phương pháp này hỗ trợ so sánh mảng, bộ đệm mảng, boolean, đối tượng ngày tháng, đối tượng lỗi, bản đồ, số, Đối tượng đối tượng, regexes, bộ, chuỗi, ký hiệu và mảng đã nhập. Các đối tượng đối tượng được so sánh bởi các thuộc tính của riêng chúng, không kế thừa, có thể liệt kê. Các hàm và các nút DOM được so sánh bằng cách bình đẳng nghiêm ngặt, tức là ===.

https://lodash.com/docs/4.17.11#isEqual

Với vấn đề ban đầu - các khóa được sắp xếp không nhất quán - đó là một giải pháp tuyệt vời - và tất nhiên nó sẽ chỉ dừng lại nếu phát hiện ra xung đột thay vì tuần tự hóa toàn bộ đối tượng một cách mù quáng.

Để tránh nhập toàn bộ thư viện, bạn làm như sau:

import { isEqual } from "lodash-es";

Ví dụ về phần thưởng: Bạn cũng có thể sử dụng điều này với RxJS với toán tử tùy chỉnh này

export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => 
                                                pipe(distinctUntilChanged(isEqual));

0

Rốt cuộc, nó cần một Mảng lưu trữ tất cả các khóa trong đối tượng lồng nhau (nếu không nó sẽ bỏ qua các khóa chưa được lưu.) Câu trả lời cũ nhất là hoàn toàn sai, bởi vì đối số thứ hai không quan tâm đến ký hiệu dấu chấm. Vì vậy, câu trả lời (sử dụng Set) trở thành.

function stableStringify (obj) {
  const keys = new Set()
  const getAndSortKeys = (a) => {
    if (a) {
      if (typeof a === 'object' && a.toString() === '[object Object]') {
        Object.keys(a).map((k) => {
          keys.add(k)
          getAndSortKeys(a[k])
        })
      } else if (Array.isArray(a)) {
        a.map((el) => getAndSortKeys(el))
      }
    }
  }
  getAndSortKeys(obj)
  return JSON.stringify(obj, Array.from(keys).sort())
}

0

Đây là một cách tiếp cận sao chép ... sao chép đối tượng trước khi chuyển đổi thành json:

function sort(o: any): any {
    if (null === o) return o;
    if (undefined === o) return o;
    if (typeof o !== "object") return o;
    if (Array.isArray(o)) {
        return o.map((item) => sort(item));
    }
    const keys = Object.keys(o).sort();
    const result = <any>{};
    keys.forEach((k) => (result[k] = sort(o[k])));
    return result;
}

Nếu rất mới nhưng có vẻ hoạt động tốt trên các tệp package.json.


-3

Có một Array.sortphương pháp có thể hữu ích cho bạn. Ví dụ:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});

1
Tôi không muốn sắp xếp mảng mặc dù. Thực ra phần mảng không quan trọng .. Tôi muốn sắp xếp các thuộc tính của một đối tượng .. từ một vài thử nghiệm nhanh, có vẻ như các thuộc tính được liệt kê theo thứ tự mà chúng được tạo. Một cách có thể là tạo một đối tượng mới và sao chép các thuộc tính theo thứ tự bảng chữ cái nhưng tôi hy vọng có thứ gì đó dễ dàng hơn / nhanh hơn ..
Innovine

1
@Innovine, ngay cả điều đó sẽ không hoạt động vì các khóa không được đảm bảo sắp xếp theo thời gian tạo bởi thông số kỹ thuật.
Dogbert

Sau đó, câu hỏi trở thành, làm thế nào để tôi xâu chuỗi các đối tượng của mình theo cách mà các khóa theo một thứ tự cố định .. tôi không thể làm cho (khóa trong obj) và lưu trữ chúng trong một mảng tạm thời và sắp xếp theo cách thủ công xây dựng một chuỗi .. nhưng tôi tuyệt vọng hy vọng có một cách dễ dàng hơn
Innovine

Không, đó là khá nhiều cách để làm điều đó. Chào mừng đến với JavaScript.
markyzm
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.