programing

객체 속성 및 JSON.stringify 정렬

nasanasas 2020. 10. 26. 08:04
반응형

객체 속성 및 JSON.stringify 정렬


내 응용 프로그램에는 많은 객체가 있으며,이를 문자열 화하여 디스크에 저장합니다. 불행히도 배열의 객체가 조작되고 때때로 교체 될 때 객체의 속성은 다른 순서 (생성 순서?)로 나열됩니다. 배열에서 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.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}

나는 당신이 JSON 생성을 제어하고 있다면 (그리고 당신처럼 들린다) 당신의 목적을 위해 이것은 좋은 해결책이 될 것이라고 생각합니다 : json-stable-stringify

프로젝트 웹 사이트에서 :

문자열 화 된 결과에서 결정적 해시를 얻기 위해 사용자 정의 정렬을 사용하는 결정적 JSON.stringify ()

생성 된 JSON이 결정적이라면 쉽게 비교 / 병합 할 수 있어야합니다.


정렬 된 속성 이름 배열을의 두 번째 인수로 전달할 수 있습니다 JSON.stringify().

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

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의 간결한 버전. https://stackoverflow.com/a/29622653/94148 에서 @codename에 대한 크레딧

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

모든 키를 재귀 적으로 가져 오기 위해 현재 베스트 답변의 복잡성이 필요한 이유를 이해할 수 없습니다. 완벽한 성능이 필요하지 않는 한 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 );
}

이것은 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"}"

toJSON출력을 사용자 지정하는 데 사용할 수 있는 사용자 지정 함수를 개체에 추가 할 수 있습니다. 함수 내에서 특정 순서로 새 객체에 현재 속성을 추가하면 문자열 화 될 때 해당 순서를 유지해야합니다.

여기를 보아라:

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

JSON 데이터는 키로 액세스 할 수 있기 때문에 순서를 제어하는 ​​내장 방법이 없습니다.

다음은 작은 예가있는 jsfiddle입니다.

http://jsfiddle.net/Eq2Yw/

toJSON함수를 주석 처리해보십시오 . 속성의 순서가 반대입니다. 이것은 브라우저에 따라 다를 수 있습니다. 즉, 주문은 사양에서 공식적으로 지원되지 않습니다. 현재 버전의 Firefox에서 작동하지만 100 % 강력한 솔루션을 원한다면 고유 한 stringifier 함수를 작성해야 할 수도 있습니다.

편집하다:

또한 stringify의 비 결정적 출력, 특히 브라우저 차이점에 대한 Daff의 세부 정보에 관한이 SO 질문을 참조하십시오.

JSON 객체가 수정되지 않았는지 결정적으로 확인하는 방법은 무엇입니까?


재귀적이고 단순화 된 대답 :

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

@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}]}

EcmaScript 2015에서 속성 이름으로 개체를 정렬 할 수 있습니다.

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

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;

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.


시험:

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

나는 객체를 정렬하는 함수를 만들었고 실제로 새로운 객체를 만드는 callback ..

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

object로 호출합니다.

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


목록의 객체에 동일한 속성이 없으면 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() );


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


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

놀랍게도 아무도 lodash의 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));

어떤 이유로 든 중첩 된 개체에 대해 허용 된 답변이 작동하지 않습니다. 이로 인해 내 자신의 코드를 작성하게되었습니다. 이 글을 쓰는 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);

-

역사적 참조를 위해이 이전 버전 유지

I found that a simple, flat array of all keys in the object will work. In almost all browsers (not Edge or Internet explorer, predictably) and Node 12+ there is a fairly short solution now that Array.prototype.flatMap(...) is available. (The lodash equivalent would work too.) I have only tested in Safari, Chrome, and Firefox, but I see no reason why it wouldn't work anywhere else that supports flatMap and standard 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);
}

With this, you can stringify with no 3rd-party dependencies and even pass in your own sort algorithm that sorts on the key-value entry pairs, so you can sort by key, payload, or a combination of the two. Works for nested objects, arrays, and any mixture of plain old data types.

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

Prints:

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

If you must have compatibility with older JavaScript engines, you could use these slightly more verbose versions that emulate flatMap behavior. Client must support at least ES5, so no Internet Explorer 8 or below.

These will return the same result as above.

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

There is Array.sort method which can be helpful for you. For example:

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

참고URL : https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify

반응형