Moja aplikacja ma dużą tablicę obiektów, które następnie modyfikuję i zapisuję na dysku. Niestety, gdy obiekty w tablicy są manipulowane, a czasami zastępowane, właściwości obiektów są wyświetlane w różnej kolejności (kolejność tworzenia?). Kiedy robię JSON.stringify () na tablicy i zapisuję ją, różnica pokazuje właściwości, które są wyświetlane w różnych kolejności, co jest denerwujące przy próbie dalszego scalania danych za pomocą narzędzi do porównywania i scalania.
Idealnie chciałbym posortować właściwości obiektów w kolejności alfabetycznej przed wykonaniem stringify lub jako część operacji stringify. Istnieje kod służący do manipulowania obiektami tablicowymi w wielu miejscach, a zmiana ich tak, aby zawsze tworzyły właściwości w jawnej kolejności, byłaby trudna.
Sugestie byłyby mile widziane!
Skrócony przykład:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
Dane wyjściowe tych dwóch wywołań stringify są różne i pojawiają się w różnicy moich danych, ale moja aplikacja nie dba o kolejność właściwości. Obiekty są budowane na wiele sposobów i miejsc.
źródło
Odpowiedzi:
Prostsze, nowoczesne i obecnie obsługiwane przez przeglądarkę podejście jest po prostu takie:
JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());
Jednak ta metoda usuwa wszystkie zagnieżdżone obiekty, do których nie istnieją odwołania, i nie ma zastosowania do obiektów w tablicach. Będziesz chciał spłaszczyć obiekt sortowania, jeśli chcesz czegoś takiego jak ten wynik:
{"a":{"h":4,"z":3},"b":2,"c":1}
Możesz to zrobić w ten sposób:
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());
Aby zrobić to programowo za pomocą czegoś, co możesz dostosować samodzielnie, musisz wypchnąć nazwy właściwości obiektu do tablicy, a następnie posortować tablicę alfabetycznie i powtórzyć tę tablicę (która będzie we właściwej kolejności) i wybrać każdą wartość z obiektu w to zamówienie. Opcja „hasOwnProperty” jest również zaznaczona, więc na pewno masz tylko własne właściwości obiektu. Oto przykład:
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 });
Powinno to zagwarantować, że będziesz iterować w kolejności alfabetycznej.
Wreszcie, idąc dalej w najprostszy sposób, ta biblioteka pozwoli ci rekurencyjnie posortować każdy przekazany do niej 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));
Wynik
{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
źródło
obj[i]
w pętli for, ponieważi
jest to liczba całkowita, a nazwy właściwości niekoniecznie są (stąd to pytanie). Tak powinno byćobj[arr[i]]
.iterateObjectAlphabetically
się w funkcję wielokrotnego użytku. Bez wywołania zwrotnego „kod ładunku” musiałby znajdować się wewnątrz samej funkcji. Myślę, że jest to bardzo eleganckie rozwiązanie, które jest używane w wielu bibliotekach i samym rdzeniu JS. Np . Array.forEach .Myślę, że jeśli kontrolujesz generowanie JSON (i brzmi tak, jakbyś to robił ), to dla twoich celów może być to dobre rozwiązanie: json-stable-stringify
Ze strony projektu:
Jeśli wygenerowany JSON jest deterministyczny, powinieneś być w stanie łatwo go porównać / scalić.
źródło
Nie rozumiem, dlaczego potrzebna jest złożoność aktualnie najlepszych odpowiedzi, aby uzyskać wszystkie klucze rekurencyjnie. Wydaje mi się, że o ile nie jest potrzebne perfekcyjne wykonanie, możemy po prostu zadzwonić
JSON.stringify()
dwa razy, za pierwszym razem, aby zdobyć wszystkie klucze, a za drugim, aby naprawdę wykonać pracę. W ten sposób cała złożoność rekursji jest obsługiwanastringify
i wiemy, że zna jej treść i jak obsłużyć każdy typ obiektu: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 ); }
źródło
Object.keys()
nie jest rekurencyjna, więc jeśli masz obiekt taki jak{a: "A", b: {c: "C"} }
i wywołujesz na nim Object.keys, otrzymasz tylko kluczea
ib
, ale niec
.JSON.stringify
wie wszystko o rekurencji w każdym typie obiektu obsługiwanym przez proces serializacji JSON, czy to obiekt, czy tablica (i już implementuje logikę rozpoznawania obu), więc powinien obsługiwać wszystko poprawnie dla nas.Możesz przekazać posortowaną tablicę nazw właściwości jako drugi argument
JSON.stringify()
:JSON.stringify(obj, Object.keys(obj).sort())
źródło
JSON.stringify()
jest wywoływanyreplacer
i może być tablicą. Służy do tworzenia elementu,PropertyList
który jest następnie używanySerializeJSONObject()
do dołączania właściwości w tej kolejności. Jest to udokumentowane od ECMAScript 5.> JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}'
Jedna (hacky) sposób budowania wykaz wszystkich kluczy jest użycie JSON.stringify przechodzić obiektu nafunction orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); }
przykład:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Aktualizacja 2018-7-24:
Ta wersja sortuje obiekty zagnieżdżone i obsługuje również tablice:
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)); }
Przypadek testowy:
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}]'); }); });
Nieaktualna odpowiedź:
Zwięzła wersja w ES2016. Kredyt dla @codename, z https://stackoverflow.com/a/29622653/94148
function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
źródło
function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
sortObjByKey()
sprawdza cykliczne odwołania, więc bądź ostrożny, może zawiesić się w nieskończonej pętli w zależności od danych wejściowych. Przykład:var objA = {}; var objB = {objA: objA}; objA.objB = objB;
->sortObjByKey(objA);
->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490
const replacer = (key, value) => value instanceof Object && !(value instanceof Array) ? Object.keys(value) .sort() .reduce((sorted, key) => { sorted[key] = value[key]; return sorted }, {}) : value;
źródło
To jest to samo, co odpowiedź Satpala Singha
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"}"
źródło
Do obiektu można dodać funkcję niestandardową,
toJSON
której można użyć do dostosowania wyników. Wewnątrz funkcji dodanie bieżących właściwości do nowego obiektu w określonej kolejności powinno zachować tę kolejność, gdy zostanie poddana stringowaniu.Spójrz tutaj:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify
Nie ma wbudowanej metody kontrolowania kolejności, ponieważ dane JSON mają być dostępne za pomocą kluczy.
Oto jsfiddle z małym przykładem:
http://jsfiddle.net/Eq2Yw/
Spróbuj wykomentować
toJSON
funkcję - kolejność właściwości jest odwrócona. Należy pamiętać, że może to być specyficzne dla przeglądarki, tj. Zamawianie nie jest oficjalnie obsługiwane w specyfikacji. Działa w aktualnej wersji Firefoksa, ale jeśli potrzebujesz w 100% niezawodnego rozwiązania, być może będziesz musiał napisać własną funkcję stringifier.Edytować:
Zobacz także to pytanie SO dotyczące niedeterministycznych danych wyjściowych stringify, w szczególności szczegóły Daffa dotyczące różnic w przeglądarkach:
Jak deterministycznie zweryfikować, czy obiekt JSON nie został zmodyfikowany?
źródło
Rekurencyjna i uproszczona odpowiedź:
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);
źródło
reorder
należy zmienić nazwę na,sortObject
a to nie obsługuje tablictypeof arr === "object"
tym, że przekształciłby tablice w obiekty. Na przykładvar obj = {foo: ["bar", "baz"]}
będzie to{ "foo": { "0": "bar", "1": "baz"} }
if(obj.constructor.prototype !== Object.prototype)
Możesz sortować obiekty według nazwy właściwości w EcmaScript 2015
function sortObjectByPropertyName(obj) { return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {}); }
źródło
sortObjectPropertiesByName()
lub prostszasortPropertiesByName()
.Otrzymałem odpowiedź od @Jason Parham i wprowadziłem kilka ulepszeń
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; }
Rozwiązuje to problem konwertowania tablic na obiekty, a także umożliwia zdefiniowanie sposobu sortowania tablic.
Przykład:
var data = { content: [{id: 3}, {id: 1}, {id: 2}] }; sortObject(data, (i1, i2) => i1.id - i2.id)
wynik:
{content:[{id:1},{id:2},{id:3}]}
źródło
Zaakceptowana odpowiedź z jakiegoś powodu nie działa w przypadku obiektów zagnieżdżonych. To doprowadziło mnie do stworzenia własnego kodu. Ponieważ piszę to pod koniec 2019 roku, w języku dostępnych jest kilka innych opcji.
Aktualizacja: Uważam, że odpowiedź Davida Furlonga jest lepszym podejściem do mojej wcześniejszej próby i zlekceważyłem to. Mój polega na obsłudze Object.entries (...), więc nie ma wsparcia dla Internet Explorera.
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);
-
UTRZYMANIE TEJ STARSZEJ WERSJI DO ODNIESIENIA HISTORYCZNEGO
Odkryłem, że prosta, płaska tablica wszystkich kluczy w obiekcie będzie działać. W prawie wszystkich przeglądarkach (nie Edge ani Internet Explorer, jak można się spodziewać ) i Node 12+ istnieje dość krótkie rozwiązanie, teraz, gdy jest dostępne Array.prototype.flatMap (...) . (Odpowiednik lodash też by działał.) Testowałem tylko w Safari, Chrome i Firefox, ale nie widzę powodu, dla którego nie działałby nigdzie indziej, który obsługuje flatMap i standardowy 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); }
Dzięki temu można określić ciągi bez zależności od innych firm, a nawet przekazać własny algorytm sortowania, który sortuje według par klucz-wartość, dzięki czemu można sortować według klucza, ładunku lub ich kombinacji. Działa dla zagnieżdżonych obiektów, tablic i dowolnej mieszanki zwykłych starych typów danych.
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));
Wydruki:
{ "a": 2, "b": 1, "c": { "x": 3, "y": [ 2048, 1999, { "f": 5, "g": "help", "x": false } ], "z": 4 } }
Jeśli musisz mieć zgodność ze starszymi silnikami JavaScript , możesz użyć tych nieco bardziej szczegółowych wersji, które emulują zachowanie flatMap. Klient musi obsługiwać co najmniej ES5, więc nie ma przeglądarki Internet Explorer 8 lub starszej.
Zwrócą one taki sam wynik jak powyżej.
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); }
źródło
Działa z lodash, obiektami zagnieżdżonymi, dowolną wartością atrybutu obiektu:
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)
Opiera się na zachowaniu Chrome i Node, że pierwszy klucz przypisany do obiektu jest wyprowadzany jako pierwszy
JSON.stringify
.źródło
Próbować:
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);
źródło
Zrobiłem funkcję sortowania obiektu i wywołania zwrotnego .. która faktycznie tworzy nowy obiekt
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; },{}); }
i nazwij to obiektem.
var obj = { name : "anu", os : "windows", value : 'msio', }; var result = sortObj( obj , function( a, b ){ return a.key < b.key ; }); JSON.stringify( result )
które drukuje
{"value":"msio","os":"windows","name":"anu"}
i do sortowania według wartości.var result = sortObj( obj , function( a, b ){ return a.value < b.value ; }); JSON.stringify( result )
który drukuje
{"os":"windows","value":"msio","name":"anu"}
źródło
Jeśli obiekty na liście nie mają takich samych właściwości, wygeneruj połączony obiekt główny przed 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() );
źródło
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 ); }
// przykład jak poniżej. w każdej warstwie, dla obiektów takich jak {}, spłaszczonych według klucza. dla tablic, liczb lub ciągów, spłaszczonych jak / z JSON.stringify.
źródło
Rozszerzenie odpowiedzi AJP o obsługę tablic:
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; }
źródło
Zaskoczony, nikt nie wspomniał o
isEqual
funkcji lodash .Przy pierwotnym problemie - niespójna kolejność kluczy - jest to świetne rozwiązanie - i oczywiście po prostu się zatrzyma, jeśli znajdzie konflikt, zamiast ślepo serializować cały obiekt.
Aby uniknąć importowania całej biblioteki, wykonaj następujące czynności:
import { isEqual } from "lodash-es";
Przykład bonusu: Możesz również użyć tego z RxJS z tym niestandardowym operatorem
export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => pipe(distinctUntilChanged(isEqual));
źródło
W końcu potrzebuje tablicy, która buforuje wszystkie klucze w zagnieżdżonym obiekcie (w przeciwnym razie pominie klucze niebuforowane). Najstarsza odpowiedź jest po prostu błędna, ponieważ drugi argument nie przejmuje się notacją kropkową. Tak więc odpowiedź (używając Set) brzmi.
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()) }
źródło
Oto podejście do klonowania ... klonuj obiekt przed konwersją do 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; }
Jeśli jest bardzo nowy, ale wydaje się działać na plikach package.json w porządku.
źródło
Jest
Array.sort
metoda, która może ci pomóc. Na przykład:yourBigArray.sort(function(a,b){ //custom sorting mechanism });
źródło