сортувати властивості об’єкта та JSON.stringify


94

У моїй програмі є великий масив об’єктів, які я розтягую та зберігаю на диску. На жаль, коли об’єктами в масиві маніпулюють, а іноді і замінюють, властивості об’єктів перелічуються в різних порядках (порядок їх створення?). Коли я роблю JSON.stringify () на масиві і зберігаю його, diff показує властивості, що перелічуються в різних порядках, що дратує при подальшій злитті даних за допомогою інструментів diff та злиття.

В ідеалі я хотів би відсортувати властивості об'єктів в алфавітному порядку перед виконанням 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
Ключі об’єктів в об’єктах не гарантують фіксованого порядку. Це за задумом.
Перехожий


1
@rab, де ти це дізнався? Примітка: Це може спрацювати (і, мабуть, більшість часу), але я мав на увазі, що це не гарантовано.
Догберт

1
OP тут. Тут не було залежності від порядку власності, лише питання про те, як уникнути відмінностей у серіалізованих даних. Врешті-решт це було вирішено шляхом зберігання властивостей у масивах та їх сортування до серіалізації, подібно до прийнятої відповіді нижче.
Innovine

Відповіді:


73

Більш простий, сучасний і в даний час підтримуваний браузером підхід полягає просто в наступному:

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, оскільки iце ціле число, а назви властивостей не обов’язково (звідси це питання). Це повинно бути obj[arr[i]].
Ендрю Енслі,

1
Зворотні дзвінки не стосуються лише асинхронної поведінки.
marksyzm

2
@Johann Використовуючи тут зворотний виклик, він перетворюється iterateObjectAlphabeticallyна функцію багаторазового використання. Без зворотного виклику «код корисного навантаження» повинен знаходитися всередині самої функції. Я думаю, що це дуже елегантне рішення, яке використовується у багатьох бібліотеках і в самому ядрі JS. Наприклад, масив . Для кожного .
Stijn de Witt

1
@marksyzm Нічого страшного, у моєму випадку мені довелося строгіфікувати лише підбір полів. Таким чином, ваша відповідь поставила мене на шлях. Дякую
Бендж

39

Я думаю, що якщо ви контролюєте генерацію JSON (і це схоже на вас), то для ваших цілей це може бути хорошим рішенням: json-stable-stringify

З веб-сайту проекту:

детермінований JSON.stringify () із користувацьким сортуванням, щоб отримати детерміновані хеші зі строкових результатів

Якщо створений JSON є детермінованим, ви зможете легко його розрізнити / об'єднати.


Зверніть увагу, що цей репо є у поганому стані: не оновлювався протягом 3 років і має невирішені проблеми та запити на витягування. Я думаю, що краще звернутися до нових відповідей.
Том,

3
@Tom Також зверніть увагу, що це репо має 5 мільйонів завантажень щотижня (!). Іншими словами, він активно використовується. Наявність "невирішених" відкритих питань та / або запитів на витяг не означає багато. Тільки щоб авторів не хвилювали ці проблеми, не було часу тощо. Не кожну бібліотеку потрібно оновлювати кожні пару місяців. Насправді, найкращі бібліотеки оновлюються дуже рідко. Коли щось робиться, це робиться. Я не кажу, що у цього проекту немає реальних проблем, але якщо так, то вкажіть їх. Я не пов'язаний з цією бібліотекою BTW.
Stijn de Witt

ES6, я думаю, зламав його, маючи можливість сортувати об'єкти (не-масиви), як зазначено. Мені це потрібно для редагування користувачів. Навіть якщо порядок для комп'ютера не має значення. Це стосується користувачів, які редагують його. Варто було спробувати.
TamusJRoyce

Крім того, що говорить @Tom, я також зазначу, що цей проект не має залежностей від часу виконання. Це означає, що якщо в цьому репозиторії немає проблем із безпекою / технічним обслуговуванням, то він, мабуть, досить стабільний і безпечний у використанні. Крім того, я щойно протестував його на ES6, і, здається, він працює нормально (принаймні у Firefox).
Філ

27

Ви можете передати відсортований масив імен властивостей як другий аргумент JSON.stringify():

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

1
Чи є якась документально підтверджена гарантія такої поведінки?
Багатий Ремер

4
@richremer Так, у стандарті ECMAScript другий параметр параметра JSON.stringify()викликається replacerі може бути масивом. Він використовується для побудови а, PropertyListякий потім використовується SerializeJSONObject()для додавання властивостей у такому порядку. Це задокументовано з ECMAScript 5.
Крістіан д'Евре

14
Зверніть увагу, що якщо 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}}'
Саїф Хакім

Object.keys () не є рекурсивним. Це не буде працювати для будь-якого об'єкта, що містить вкладені масиви або об'єкти.
Йор

25

Я не розумію, навіщо потрібна складність поточних найкращих відповідей, щоб отримати всі ключі рекурсивно. Якщо не потрібна ідеальна робота, мені здається, що ми можемо просто зателефонувати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
Z. Khullah

5
Ні, справа в тому, що Object.keys()це не рекурсивно, тому якщо у вас є такий об’єкт, як {a: "A", b: {c: "C"} }ви викликаєте на ньому Object.keys, ви отримаєте лише ключі aта b, але ні c. JSON.stringifyзнає все про рекурсію для кожного типу об’єкта, що обробляється процесом серіалізації JSON, будь то об’єкт-подібний або масив (і він уже реалізує логіку для розпізнавання обох), тому він повинен обробляти все правильно для нас.
Jor

1
Люблю це рішення, здається, дуже елегантне та безпечне.
Маркус

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. Дякую.
Йогурт Мудрий

2
Зауважте, що це, як і відповідь Крістіана, поводиться з вкладеними об’єктами.
Даніель Гриском,

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

Це те саме, що відповідь Сатпала Сінгха

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% надійне рішення, можливо, вам доведеться написати власну функцію розтягування.

Редагувати:

Також див. Це запитання SO щодо недетермінованого виводу stringify, особливо деталей Даффа про відмінності браузера:

Як детерміновано перевірити, що об’єкт JSON не був змінений?


Властивості кожного об'єкта відомі (і в основному це рядки), тому жорстке кодування властивостей у моєму власному стрингері toJSON може бути набагато швидшим, ніж сортування ...!
Innovine

'ЗамовленийJsonStringify' нижче майже працював. Але це, мабуть, є кращим рішенням для мене. Коли мені потрібно отримати '__type' C # DataContractJsonSerializer, щоб це був перший елемент у рядку json. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' тощо .....]); трохи болю, щоб мати список усіх ключів.
Йогурт Мудрий

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і це не обробляє масиви
Джонатан Гаврих

Дякуємо, що помітили це, і проблема OP полягала у використанні об’єктів, а не масивів.
Джейсон Пархем,

@JonathanGawrych Чому б ви все одно сортували масиви, вони повинні мати замовлення. Масив [1,2,3] - це не те саме, що масив [2,3,1], але об’єкт не має порядку властивостей. Проблема в тому, що порядок раптом має значення після розтягування - рядки для 100% еквівалентних об’єктів можуть бути різними. На жаль, намагання отримати детермінований stringify здається значним, приклад пакета: github.com/substack/json-stable-stringify/blob/master/index.js
Морре

1
@ Mörre - Я думаю, я не був досить конкретним. Під "не обробляє масиви" я мав на увазі масиви розбиті, а не залишені без сортування. Проблема в тому, що typeof arr === "object"це перетворило б масиви в об’єкти. Наприклад, var obj = {foo: ["bar", "baz"]}було б розмежовано на{ "foo": { "0": "bar", "1": "baz"} }
Джонатан Гаврич

виправити проблему, виявлену @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 року, коли я пишу це, у мові є ще кілька варіантів.

Оновлення: Я вважаю , що відповідь Девіда Ферлонга є кращим підходом до моєї попередньої спроби, і я відмовився від цього. 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 , ви можете використовувати ці дещо детальніші версії, які імітують поведінку 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 і Node, що перший ключ, призначений об'єкту, виводиться першим 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
Працює добре, але, на жаль, це не рекурсивно.
Джонатан Гаврич,

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


Будь ласка, поясніть, чому використовувати зовнішню бібліотеку, і, як сказав @DeKaNszn, додайте кілька пояснень та вступ. Це було б вдячно.
Benj

ця функція скопійована з мого проекту. в якому я використовую 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функцію лодаша .

Виконує глибоке порівняння двох значень, щоб визначити, чи вони еквівалентні.

Примітка: Цей метод підтримує порівняння масивів, буферів масивів, булевих значень, об’єктів дати, об’єктів помилок, карт, чисел, об’єктних об’єктів, регулярних виразів, наборів, рядків, символів та набраних масивів. Об'єкти об'єктів порівнюються за своїми, не успадкованими, незліченними властивостями. Функції та 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

Врешті-решт, йому потрібен масив, який кешує всі ключі вкладеного об'єкта (інакше некешовані ключі він опустить.) Найдавніша відповідь просто невірна, оскільки другий аргумент не має значення для крапкових позначень. Отже, відповідь (за допомогою 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 чудово.


-3

Є Array.sortметод, який може бути вам корисним. Наприклад:

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

1
Однак я не хочу сортувати масив. Насправді частина масиву не важлива .. Я хочу відсортувати властивості об'єкта .. з кількох швидких експериментів здається, що властивості перераховані в порядку, в якому вони створені. Одним із способів може бути створення нового об'єкта та копіювання властивостей в алфавітному порядку, але я сподіваюся, що буде щось простіше / швидше ..
Innovine

1
@Innovine, навіть це не буде працювати, оскільки клавіші не гарантовано впорядковуються за часом створення специфікацією.
Догберт

Тоді виникає запитання, як мені розрізнити мої об'єкти таким чином, щоб ключі були у фіксованому порядку .. мені неможливо зробити for (key in obj) і зберегти їх у тимчасовому масиві та відсортувати що і вручну побудувати рядок .. але я відчайдушно сподіваюся, що є простіший спосіб
Innovine

Ні, це майже спосіб зробити це. Ласкаво просимо до JavaScript.
marksyzm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.