คุณสมบัติการจัดเรียงวัตถุและ JSON.stringify


108

แอปพลิเคชันของฉันมีอาร์เรย์ของอ็อบเจ็กต์จำนวนมากซึ่งฉันทำให้สตริงและบันทึกลงในดิสก์ น่าเสียดายที่เมื่อวัตถุในอาร์เรย์ถูกจัดการและบางครั้งถูกแทนที่คุณสมบัติของวัตถุจะแสดงรายการในลำดับที่แตกต่างกัน (ลำดับการสร้าง?) เมื่อฉันทำ JSON.stringify () บนอาร์เรย์และบันทึกความแตกต่างจะแสดงคุณสมบัติที่แสดงรายการในคำสั่งซื้อที่แตกต่างกันซึ่งน่ารำคาญเมื่อพยายามผสานข้อมูลเพิ่มเติมด้วยเครื่องมือต่างและการรวม

ตามหลักการแล้วฉันต้องการจัดเรียงคุณสมบัติของวัตถุตามลำดับตัวอักษรก่อนที่จะดำเนินการ stringify หรือเป็นส่วนหนึ่งของการดำเนินการ stringify มีรหัสสำหรับจัดการวัตถุอาร์เรย์ในหลาย ๆ ที่และการแก้ไขสิ่งเหล่านี้เพื่อสร้างคุณสมบัติตามลำดับที่ชัดเจนอาจเป็นเรื่องยาก

ข้อเสนอแนะจะยินดีมากที่สุด!

ตัวอย่างย่อ:

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

ผลลัพธ์ของการเรียก stringify ทั้งสองนี้แตกต่างกันและแสดงในข้อมูลที่แตกต่างกัน แต่แอปพลิเคชันของฉันไม่สนใจลำดับของคุณสมบัติ วัตถุถูกสร้างขึ้นในหลายวิธีและหลายสถานที่


โปรดยกตัวอย่างออบเจ็กต์ที่คุณกำลังพยายามสตริง (เอาต์พุต JSON หรืออ็อบเจกต์ลิเทอรัล JS)
Bojangles

4
ไม่รับประกันว่าคีย์ออบเจ็กต์ในอ็อบเจ็กต์จะมีลำดับที่ตายตัว นี่คือการออกแบบ
Passerby


1
@rab คุณหาข้อมูลนั้นมาจากไหน? หมายเหตุ: สามารถใช้งานได้ (และอาจใช้เวลาเกือบตลอดเวลา) แต่ฉันหมายความว่าไม่สามารถรับประกันได้
Dogbert

1
OP ที่นี่ ไม่มีการพึ่งพาลำดับคุณสมบัติที่นี่มีเพียงคำถามเกี่ยวกับวิธีหลีกเลี่ยงความแตกต่างในข้อมูลที่ทำให้เป็นอนุกรม ในที่สุดสิ่งนี้ได้รับการแก้ไขโดยการซ่อนคุณสมบัติในอาร์เรย์และจัดเรียงก่อนที่จะทำให้เป็นอนุกรมเหมือนกับในคำตอบที่ยอมรับด้านล่าง
Innovine

คำตอบ:


84

แนวทางที่ง่ายกว่าทันสมัยและรองรับเบราว์เซอร์ในปัจจุบันมีดังนี้:

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

อย่างไรก็ตามวิธีนี้จะลบอ็อบเจ็กต์ที่ซ้อนกันที่ไม่ได้อ้างอิงและไม่ได้ใช้กับอ็อบเจ็กต์ภายในอาร์เรย์ คุณจะต้องแบนวัตถุการเรียงลำดับเช่นกันหากคุณต้องการผลลัพธ์เช่นนี้:

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

คุณสามารถทำได้ด้วยสิ่งนี้:

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

ในการดำเนินการทางโปรแกรมด้วยสิ่งที่คุณสามารถปรับแต่งได้เองคุณต้องพุชชื่อคุณสมบัติของวัตถุลงในอาร์เรย์จากนั้นเรียงลำดับอาร์เรย์ตามตัวอักษรและวนซ้ำผ่านอาร์เรย์นั้น (ซึ่งจะอยู่ในลำดับที่ถูกต้อง) และเลือกแต่ละค่าจากวัตถุใน คำสั่งนั้น. มีการตรวจสอบ "hasOwnProperty" ด้วยดังนั้นคุณจึงมีคุณสมบัติของวัตถุเท่านั้น นี่คือตัวอย่าง:

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

อีกครั้งควรรับประกันว่าคุณจะวนซ้ำตามลำดับตัวอักษร

สุดท้ายนำไปใช้ต่อไปสำหรับวิธีที่ง่ายที่สุดไลบรารีนี้จะช่วยให้คุณสามารถเรียงลำดับ JSON ที่คุณส่งผ่านเข้าไปซ้ำได้: 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));

เอาต์พุต

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

9
นี่เป็นสิ่งที่ฉันพยายามหลีกเลี่ยงมาก :) แต่ขอบคุณดูเหมือนวิธีแก้ปัญหาที่ถูกต้องถ้าหนัก
Innovine

1
มีข้อบกพร่องในรหัสนี้ คุณไม่สามารถอ้างอิงobj[i]ใน for loop ได้เนื่องจากiเป็นจำนวนเต็มและไม่จำเป็นต้องใช้ชื่อคุณสมบัติ (ดังนั้นคำถามนี้) obj[arr[i]]มันควรจะเป็น
Andrew Ensley

1
การโทรกลับไม่ได้เป็นเอกสิทธิ์ของพฤติกรรมแบบอะซิงโครนัส
markyzm

2
@Johann โดยใช้การโทรกลับที่นี่เขาจะเปลี่ยนiterateObjectAlphabeticallyเป็นฟังก์ชันที่ใช้ซ้ำได้ หากไม่มีการเรียกกลับ 'รหัส payload' จะต้องอยู่ในฟังก์ชันนั้นเอง ฉันคิดว่ามันเป็นโซลูชันที่ยอดเยี่ยมมากและเป็นโซลูชันที่ใช้ในไลบรารีจำนวนมากและ JS core เอง เช่นArray.forEach .
Stijn de Witt

1
@marksyzm ไม่เป็นไรในกรณีของฉันฉันต้องสตริงเฉพาะฟิลด์ที่เลือก ดังนั้นคำตอบของคุณทำให้ฉันรู้ ขอบคุณ
Benj

39

ฉันคิดว่าถ้าคุณควบคุมการสร้าง JSON (และดูเหมือนว่าคุณเป็น) ดังนั้นสำหรับจุดประสงค์ของคุณนี่อาจเป็นทางออกที่ดี: json-stable-stringify

จากเว็บไซต์โครงการ:

JSON.stringify () ที่กำหนดด้วยการจัดเรียงแบบกำหนดเองเพื่อรับแฮชที่กำหนดจากผลลัพธ์ที่เป็นสตริง

หาก JSON ที่สร้างขึ้นนั้นถูกกำหนดคุณควรจะสามารถแตกต่าง / รวมเข้าด้วยกันได้อย่างง่ายดาย


1
โปรดทราบว่า repo มีรูปร่างไม่ดี: ไม่มีการอัปเดตใน 3 ปีและมีปัญหาที่ยังไม่ได้รับการแก้ไขและคำขอดึง ฉันคิดว่ามันจะดีกว่าหากมองหาคำตอบที่ใหม่กว่านี้
ทอม

3
@Tom โปรดทราบว่า repo มีการดาวน์โหลด (!) 5 ล้านครั้งต่อสัปดาห์ กล่าวอีกนัยหนึ่งก็คือมีการใช้งานอย่างจริงจัง การมีปัญหาที่เปิดอยู่ 'ยังไม่ได้รับการแก้ไข' และ / หรือคำขอดึงไม่ได้มีความหมายมากนัก เพียงแค่ว่าผู้เขียนไม่สนใจปัญหาเหล่านั้นหรือไม่มีเวลาเป็นต้นไม่ใช่ทุก lib ที่จะต้องได้รับการอัปเดตทุกสองสามเดือน ในความเป็นจริง imho libs ที่ดีที่สุดจะได้รับการอัปเดตไม่บ่อยนัก เมื่อทำอะไรเสร็จก็ทำ ฉันไม่ได้บอกว่าโครงการนี้ไม่มีปัญหาจริงๆ แต่ถ้าเป็นเช่นนั้นโปรดชี้ให้เห็น ฉันไม่มีส่วนเกี่ยวข้องกับ lib BTW นี้
Stijn de Witt

ES6 ฉันเดาว่ามันไม่สามารถจัดเรียงวัตถุ (ไม่ใช่อาร์เรย์) ได้ตามที่ระบุ ฉันต้องการสิ่งนี้สำหรับการแก้ไขของผู้ใช้ แม้ว่าการสั่งซื้อจะไม่สำคัญกับคอมพิวเตอร์ก็ตาม ไม่ให้ผู้ใช้แก้ไข มันคุ้มค่าที่จะลอง
TamusJRoyce

1
นอกเหนือจากสิ่งที่ @Tom พูดแล้วฉันจะชี้ให้เห็นด้วยว่าโครงการนี้ไม่มีการพึ่งพารันไทม์ หมายความว่าหากไม่มีปัญหาด้านความปลอดภัย / การบำรุงรักษาใน repo นี้ก็น่าจะค่อนข้างเสถียรและปลอดภัยในการใช้งาน นอกจากนี้ฉันเพิ่งทดสอบกับ ES6 และดูเหมือนว่าจะทำงานได้ดี (อย่างน้อยใน Firefox)
ฟิลิป

30

ฉันไม่เข้าใจว่าเหตุใดจึงต้องมีความซับซ้อนของคำตอบที่ดีที่สุดในปัจจุบันเพื่อรับคีย์ทั้งหมดแบบวนซ้ำ สำหรับฉันแล้วดูเหมือนว่าเราสามารถโทรได้JSON.stringify()สองครั้งครั้งแรกเพื่อรับคีย์ทั้งหมดและครั้งที่สองเพื่อทำงานจริงๆ ด้วยวิธีนี้ความซับซ้อนของการเรียกซ้ำทั้งหมดจะถูกจัดการstringifyและเรารู้ว่ามันรู้สิ่งต่างๆของมันและวิธีจัดการกับออบเจ็กต์แต่ละประเภท:

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

นั่นน่าสนใจ แต่จริงๆแล้วคุณ "โกง" โดยใช้ stringify เพื่อแมปคีย์ทั้งหมดกับอาร์เรย์ อาร์เรย์นั้นสามารถรับได้ง่ายขึ้นดีขึ้นและปลอดภัยขึ้นผ่าน Object.keys
Bernardo Dal Corno

6
ไม่มีประเด็นก็คือว่าObject.keys()จะไม่ซ้ำดังนั้นหากคุณมีวัตถุเช่น{a: "A", b: {c: "C"} }และคุณเรียก Object.keys ในนั้นคุณจะได้รับกุญแจaและแต่ไม่b รู้ทุกอย่างเกี่ยวกับการเรียกซ้ำในวัตถุทุกประเภทที่จัดการโดยกระบวนการอนุกรม JSON ไม่ว่าจะเป็นวัตถุเหมือนหรืออาร์เรย์ (และใช้ตรรกะในการรับรู้ทั้งสองอย่างแล้ว) ดังนั้นจึงควรจัดการทุกอย่างให้ถูกต้องสำหรับเรา cJSON.stringify
จ.

1
ชอบวิธีนี้มากดูสง่างามและไม่ปลอดภัย
Markus

@ ขอบคุณที่ทำงานให้ฉัน ตัวอย่างข้อมูลอื่น ๆ ที่ฉันพยายามล้มเหลวไม่ทางใดก็ทางหนึ่ง
nevf

ฉลาด! re perf: ฉันเชื่อว่านี่คือ O (N ^ 2), N = คีย์ตัวเลขในออบเจ็กต์เนื่องจากคีย์ที่เรียงลำดับจะถูกตรวจสอบแบบเชิงเส้นสำหรับทุกคีย์ในวัตถุ ดังนั้นความสมบูรณ์แบบจะเริ่มเป็นปัญหาที่สังเกตเห็นได้ชัดเจนเกี่ยวกับ N> 100K บนเครื่อง GHz และอาจทำให้ร่างกายอ่อนแอลงสำหรับ N> 1Million ref: tc39.es/ecma262/#sec-json.stringify
Mike Liddell

28

คุณสามารถส่งอาร์เรย์ที่เรียงลำดับของชื่อคุณสมบัติเป็นอาร์กิวเมนต์ที่สองของJSON.stringify():

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

1
มีเอกสารรับรองเกี่ยวกับพฤติกรรมนี้หรือไม่?
rich remer

4
@richremer ใช่ในมาตรฐาน ECMAScriptพารามิเตอร์ที่สองJSON.stringify()ถูกเรียกreplacerและสามารถเป็นอาร์เรย์ได้ มันถูกใช้เพื่อสร้างPropertyListซึ่งจะใช้ในSerializeJSONObject()การต่อท้ายคุณสมบัติตามลำดับนั้น เอกสารนี้บันทึกไว้ตั้งแต่ ECMAScript 5.
Christian d'Heureuse

15
โปรดสังเกตว่าถ้า obj มีอ็อบเจ็กต์ที่ซ้อนกันอยู่คีย์ที่ซ้อนกันจะต้องมีอยู่ในอาร์กิวเมนต์ที่สองด้วย มิฉะนั้นจะถูกทิ้ง! ตัวอย่างการตอบโต้: วิธี> JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' หนึ่ง (แฮ็ก) ในการสร้างรายการคีย์ที่เกี่ยวข้องทั้งหมดคือการใช้ JSON.stringify เพื่อสำรวจวัตถุ: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } ตัวอย่าง:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim

1
Object.keys () ไม่วนซ้ำ สิ่งนี้จะใช้ไม่ได้กับวัตถุใด ๆ ที่มีอาร์เรย์หรือวัตถุที่ซ้อนกัน

14

อัปเดต 2018-7-24:

รุ่นนี้จัดเรียงวัตถุที่ซ้อนกันและรองรับอาร์เรย์ด้วย:

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

กรณีทดสอบ:

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

  });

คำตอบที่ไม่รองรับ:

ฉบับกระชับใน ES2016 เครดิต @codename จากhttps://stackoverflow.com/a/29622653/94148

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

ไวยากรณ์ไม่ถูกต้อง ควรจะเป็น -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
เบ็น

สำหรับตอนนี้สิ่งนี้ได้แก้ไขปัญหาของฉันกับ C # DataContractJsonSerializer และ "__type" ไม่ปรากฏเป็นอันดับแรกในสตริง json ขอบคุณ.
Yogurt The Wise

2
โปรดสังเกตว่าสิ่งนี้เช่นเดียวกับคำตอบของคริสเตียนที่ใช้วัตถุซ้อนกันอย่างไม่ถูกต้อง
Daniel Griscom

ไม่ได้กำหนด sortObjPropertiesByKey คุณหมายถึงใช้ sortObjByKey หรือไม่?
Paul Lynch

ดูเหมือนว่าสิ่งนี้sortObjByKey()จะไม่ตรวจสอบการอ้างอิงแบบวงกลมดังนั้นโปรดระวังมันอาจค้างอยู่ในวงวนไม่สิ้นสุดขึ้นอยู่กับข้อมูลอินพุต ตัวอย่าง: var objA = {}; var objB = {objA: objA}; objA.objB = objB;-> sortObjByKey(objA);->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
Klesun


4

นี่เหมือนกับคำตอบของ 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

คุณสามารถเพิ่มtoJSONฟังก์ชันที่กำหนดเองให้กับวัตถุของคุณซึ่งคุณสามารถใช้เพื่อปรับแต่งผลลัพธ์ได้ ภายในฟังก์ชันการเพิ่มคุณสมบัติปัจจุบันให้กับอ็อบเจ็กต์ใหม่ในลำดับที่เจาะจงควรรักษาลำดับนั้นไว้เมื่อสตริง

ดูที่นี่:

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

ไม่มีวิธีการในตัวในการควบคุมการสั่งซื้อเนื่องจากข้อมูล JSON ถูกกำหนดให้เข้าถึงได้โดยคีย์

นี่คือ jsfiddle พร้อมตัวอย่างเล็ก ๆ :

http://jsfiddle.net/Eq2Yw/

ลองแสดงความคิดเห็นในไฟล์ toJSONฟังก์ชัน - ลำดับของคุณสมบัติจะกลับรายการ โปรดทราบว่าสิ่งนี้อาจเป็นเฉพาะเบราว์เซอร์กล่าวคือไม่รองรับการสั่งซื้ออย่างเป็นทางการในข้อกำหนด ใช้งานได้ใน Firefox เวอร์ชันปัจจุบัน แต่ถ้าคุณต้องการโซลูชันที่มีประสิทธิภาพ 100% คุณอาจต้องเขียนฟังก์ชัน stringifier ของคุณเอง

แก้ไข:

นอกจากนี้โปรดดูคำถาม SO นี้เกี่ยวกับเอาต์พุตที่ไม่ได้กำหนดของ stringify โดยเฉพาะรายละเอียดของ Daff เกี่ยวกับความแตกต่างของเบราว์เซอร์:

จะตรวจสอบได้อย่างไรว่าออบเจ็กต์ JSON ไม่ได้รับการแก้ไข


คุณสมบัติของแต่ละวัตถุเป็นที่รู้จัก (และส่วนใหญ่เป็นสตริง) ดังนั้นการเข้ารหัสคุณสมบัติในสตริง toJSON ของฉันเองอาจเร็วกว่าการเรียงลำดับ ... !
Innovine

'orderJsonStringify' ด้านล่างเกือบใช้งานได้ แต่ดูเหมือนจะเป็นทางออกที่ดีกว่าสำหรับฉัน เมื่อฉันต้องการรับ '__type' ของ C # DataContractJsonSerializer เป็นรายการแรกในสตริง json var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' ฯลฯ ..... ]); เจ็บเล็กน้อยที่ต้องทำรายการคีย์ทั้งหมด
Yogurt The Wise

3

คำตอบแบบเรียกซ้ำและเข้าใจง่าย:

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

reorderควรเปลี่ยนชื่อเป็นsortObjectและไม่จัดการอาร์เรย์
Jonathan Gawrych

ขอขอบคุณที่สังเกตเห็นว่าปัญหาของ OP คือการใช้อ็อบเจ็กต์ไม่ใช่อาร์เรย์
Jason Parham

@JonathanGawrych ทำไมคุณถึงจัดเรียงอาร์เรย์ต่อไปพวกเขาควรจะมีคำสั่ง อาร์เรย์ [1,2,3] ไม่เหมือนกับอาร์เรย์ [2,3,1] แต่อ็อบเจ็กต์ไม่มีลำดับคุณสมบัติ ปัญหาคือจู่ๆลำดับก็มีความสำคัญหลังจากการกำหนดสตริง - สตริงสำหรับวัตถุที่เทียบเท่ากัน 100% อาจแตกต่างกัน น่าเสียดายที่ความพยายามในการสร้างสตริงที่กำหนดได้ดูเหมือนจะมีความสำคัญมากตัวอย่างเช่นgithub.com/substack/json-stable-stringify/blob/master/index.js
Mörre

1
@ Mörre - ฉันเดาว่าฉันไม่เจาะจงมากพอ โดย "ไม่จัดการอาร์เรย์" ฉันหมายความว่าอาร์เรย์เสียไม่ใช่ไม่ถูกจัดเรียง ปัญหาคือเพราะtypeof arr === "object"มันจะเปลี่ยนอาร์เรย์เป็นวัตถุ ตัวอย่างเช่นvar obj = {foo: ["bar", "baz"]}จะถูกกำหนดให้เป็น{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych

เพื่อแก้ไขปัญหาที่พบโดย @JonathanGawrych:if(obj.constructor.prototype !== Object.prototype)
ricka

3

คุณสามารถจัดเรียงวัตถุตามชื่อคุณสมบัติใน EcmaScript 2015

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

อาจจะเป็นชื่อที่ดีกว่าsortObjectPropertiesByName()หรือง่ายsortPropertiesByName()กว่านั้น
ม.ค.

3

ฉันได้รับคำตอบจาก @Jason Parham และทำการปรับปรุงบางอย่าง

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

วิธีนี้แก้ไขปัญหาอาร์เรย์ที่ถูกแปลงเป็นอ็อบเจ็กต์และยังช่วยให้คุณกำหนดวิธีการจัดเรียงอาร์เรย์ได้อีกด้วย

ตัวอย่าง:

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

เอาต์พุต:

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

2

คำตอบที่ยอมรับใช้ไม่ได้สำหรับฉันสำหรับวัตถุที่ซ้อนกันด้วยเหตุผลบางประการ สิ่งนี้ทำให้ฉันต้องเขียนโค้ดของตัวเอง เนื่องจากเป็นช่วงปลายปี 2019 เมื่อฉันเขียนสิ่งนี้จึงมีตัวเลือกเพิ่มเติมอีกสองสามอย่างในภาษานี้

อัปเดต: ฉันเชื่อว่าคำตอบของ David Furlongเป็นแนวทางที่ดีกว่าสำหรับความพยายามก่อนหน้านี้ของฉันและฉันได้แยกแยะสิ่งนั้นออกไปแล้ว Mine อาศัยการสนับสนุน Object.entries (... ) ดังนั้นจึงไม่รองรับ 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);

-

การรักษาเวอร์ชันเก่านี้ไว้เพื่อการอ้างอิงทางประวัติศาสตร์

ฉันพบว่าอาร์เรย์ที่เรียบง่ายและแบนของคีย์ทั้งหมดในวัตถุจะทำงานได้ ในเบราว์เซอร์เกือบทั้งหมด (ไม่ใช่ Edge หรือ Internet explorer คาดเดาได้) และ Node 12+ มีวิธีแก้ปัญหาที่ค่อนข้างสั้นในขณะนี้ที่Array.prototype.flatMap (... )พร้อมใช้งาน (เทียบเท่า lodash จะทำงานเกินไป.) ผมได้ทดสอบเฉพาะใน Safari, Chrome และ Firefox แต่ฉันเห็นเหตุผลว่าทำไมมันจะไม่ทำงานที่ใดไม่มีที่สนับสนุน flatMap และมาตรฐาน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);
}

ด้วยวิธีนี้คุณสามารถสตริงโดยไม่มีการอ้างอิงของบุคคลที่สามและยังส่งผ่านอัลกอริทึมการจัดเรียงของคุณเองที่เรียงลำดับตามคู่รายการคีย์ - ค่าดังนั้นคุณจึงสามารถจัดเรียงตามคีย์เพย์โหลดหรือทั้งสองอย่างรวมกันได้ ใช้ได้กับอ็อบเจ็กต์อาร์เรย์ที่ซ้อนกันและส่วนผสมของชนิดข้อมูลเก่าล้วน

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

พิมพ์:

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

หากคุณต้องมีความเข้ากันได้กับเอ็นจิ้นJavaScript รุ่นเก่าคุณสามารถใช้เวอร์ชัน verbose เพิ่มเติมเล็กน้อยเหล่านี้เพื่อเลียนแบบพฤติกรรม flatMap ไคลเอนต์ต้องรองรับ ES5 เป็นอย่างน้อยดังนั้นจึงไม่มี Internet Explorer 8 หรือต่ำกว่า

สิ่งเหล่านี้จะส่งคืนผลลัพธ์เช่นเดียวกับด้านบน

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

ทำงานร่วมกับ lodash วัตถุที่ซ้อนกันค่าใด ๆ ของแอตทริบิวต์วัตถุ:

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)

มันอาศัยอยู่กับพฤติกรรมของ Chrome JSON.stringifyและโหนดว่ากุญแจดอกแรกที่ได้รับมอบหมายไปยังวัตถุที่มีการออกมาครั้งแรกโดย


2
ขอบคุณ แต่ฉันมีปัญหานี้เมื่อ 4 ปีก่อนฉันยินดีที่จะบอกว่าฉันได้ย้ายไปแล้ว :)
Innovine

1
:) ยินดีที่ได้รับฟัง แม้ว่าฉันจะมีปัญหานี้เมื่อวานนี้และไม่พบคำตอบที่ฉันต้องการภายใต้คำถาม (เก่า แต่ยังคงเกี่ยวข้อง) ของคุณ :)
AJP

0

ลอง:

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

ขอบคุณ แต่เห็นได้ชัดว่าการสั่งซื้อนั้นไม่ได้กำหนดไว้และจะใช้ได้ผลเท่านั้น แนวทางที่น่าสนใจ!
Innovine

0

ฉันสร้างฟังก์ชันเพื่อจัดเรียงวัตถุและด้วยการเรียกกลับ .. ซึ่งสร้างวัตถุใหม่ขึ้นมา

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

และเรียกมันด้วยวัตถุ

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

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

JSON.stringify( result )

ซึ่งพิมพ์{"value":"msio","os":"windows","name":"anu"}และสำหรับการเรียงลำดับด้วยค่า

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

JSON.stringify( result )

ซึ่งพิมพ์ {"os":"windows","value":"msio","name":"anu"}


1
ทำงานได้ดี แต่น่าเสียดายที่มันไม่เกิดซ้ำ
Jonathan Gawrych

0

หากอ็อบเจ็กต์ในรายการไม่มีคุณสมบัติเหมือนกันให้สร้างอ็อบเจ็กต์หลักที่รวมกันก่อน stringify:

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

// ตัวอย่างด้านล่าง ในแต่ละเลเยอร์สำหรับออบเจ็กต์เช่น {} จะแบนตามลำดับคีย์ สำหรับอาร์เรย์ตัวเลขหรือสตริงแบนเหมือน / ด้วย 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} "


โปรดอธิบายว่าเหตุใดจึงใช้ lib ภายนอกและตามที่ @DeKaNszn กล่าวเพิ่มคำอธิบายและบทนำ มันจะได้รับการชื่นชม
เบญจ

ฟังก์ชั่นนี้คัดลอกมาจากโครงการของฉัน ซึ่งฉันใช้ underscore.js
saintthor

0

การขยายคำตอบของ AJP เพื่อจัดการอาร์เรย์:

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

ไม่มีใครแปลกใจที่พูดถึงisEqualฟังก์ชั่นของ lodash

ทำการเปรียบเทียบเชิงลึกระหว่างค่าสองค่าเพื่อพิจารณาว่าค่าเท่ากันหรือไม่

หมายเหตุ: วิธีนี้สนับสนุนการเปรียบเทียบอาร์เรย์บัฟเฟอร์อาร์เรย์บูลีนวัตถุวันที่วัตถุข้อผิดพลาดแผนที่ตัวเลขวัตถุวัตถุ regexes ชุดสตริงสัญลักษณ์และอาร์เรย์ที่พิมพ์ ออบเจ็กต์วัตถุจะถูกเปรียบเทียบโดยคุณสมบัติของมันเองซึ่งไม่ได้รับการสืบทอด ฟังก์ชันและโหนด DOM ถูกเปรียบเทียบโดยความเท่าเทียมกันอย่างเข้มงวดนั่นคือ ===

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

ด้วยปัญหาดั้งเดิม - คีย์ถูกเรียงลำดับไม่สอดคล้องกัน - เป็นวิธีแก้ปัญหาที่ยอดเยี่ยม - และแน่นอนว่ามันจะหยุดหากพบข้อขัดแย้งแทนที่จะจัดลำดับวัตถุทั้งหมดแบบสุ่มสี่สุ่มห้า

เพื่อหลีกเลี่ยงการนำเข้าไลบรารีทั้งหมดให้ทำดังนี้

import { isEqual } from "lodash-es";

ตัวอย่างโบนัส: คุณสามารถใช้สิ่งนี้กับ RxJS ด้วยตัวดำเนินการที่กำหนดเองนี้ได้

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

0

ท้ายที่สุดมันต้องการ Array ที่เก็บคีย์ทั้งหมดในวัตถุที่ซ้อนกัน (มิฉะนั้นจะละเว้นคีย์ที่ไม่ได้ใส่ไว้) คำตอบที่เก่าแก่ที่สุดนั้นผิดธรรมดาเพราะอาร์กิวเมนต์ที่สองไม่สนใจเกี่ยวกับสัญลักษณ์จุด ดังนั้นคำตอบ (โดยใช้ Set) จะกลายเป็น

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

นี่คือวิธีการโคลน ... โคลนวัตถุก่อนที่จะแปลงเป็น 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;
}

ถ้าใหม่มาก แต่ดูเหมือนว่าจะทำงานกับไฟล์ package.json ได้ดี


0

อย่าสับสนกับการตรวจสอบวัตถุของโปรแกรมแก้ไขข้อบกพร่อง Chrome มันแสดงคีย์ที่เรียงในวัตถุแม้ว่าจริงๆแล้วจะไม่ได้เรียงลำดับก็ตาม คุณต้องเรียงลำดับวัตถุก่อนที่จะสตริง


ขอขอบคุณที่ชี้ให้เห็น ข้างต้นมีไว้สำหรับแอปพลิเคชัน nodejs แต่หากดีบักเกอร์ของ Chrome กำลังจัดเรียงสิ่งต่างๆด้วยตัวเองนั่นก็เป็นข้อมูลที่มีประโยชน์สำหรับใครบางคนในสักวันหนึ่ง บางทีพวกเขาอาจแสดงความคิดเห็นที่นี่เมื่อเห็นสิ่งนี้ :)
Innovine

-3

มีArray.sortวิธีการที่เป็นประโยชน์สำหรับคุณ ตัวอย่างเช่น:

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

1
ฉันไม่ต้องการเรียงลำดับอาร์เรย์ ที่จริงแล้วส่วนของอาร์เรย์ไม่สำคัญ .. ฉันต้องการจัดเรียงคุณสมบัติของวัตถุ .. จากการทดลองสั้น ๆ สองสามครั้งดูเหมือนว่าคุณสมบัติจะแสดงรายการตามลำดับที่สร้างขึ้น วิธีหนึ่งคือการสร้างวัตถุใหม่และคัดลอกคุณสมบัติตามลำดับตัวอักษร แต่ฉันหวังว่าจะมีอะไรที่ง่าย / เร็วกว่านี้ ..
Innovine

1
@Innovine แม้ว่าจะไม่สามารถใช้งานได้เนื่องจากคีย์จะไม่รับประกันว่าจะเรียงลำดับตามเวลาในการสร้างตามข้อมูลจำเพาะ
Dogbert

จากนั้นคำถามจะกลายเป็นว่าฉันจะสตริงวัตถุของฉันอย่างไรเพื่อให้คีย์อยู่ในลำดับคงที่ .. มันเป็นไปไม่ได้ที่ฉันจะทำเพื่อ (คีย์ใน obj) และจัดเก็บไว้ในอาร์เรย์ชั่วคราวและจัดเรียงสิ่งนั้นด้วยตนเอง สร้างสตริง .. แต่ฉันหวังเป็นอย่างยิ่งว่าจะมีวิธีที่ง่ายกว่านี้
Innovine

ไม่นั่นเป็นวิธีที่ดีมาก ยินดีต้อนรับสู่ JavaScript
markyzm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.