オブジェクトプロパティのソートとJSON.stringify


94

私のアプリケーションには大量のオブジェクトがあり、それを文字列化してディスクに保存します。残念ながら、配列内のオブジェクトが操作され、場合によっては置換されると、オブジェクトのプロパティは異なる順序(作成順序?)でリストされます。配列に対してJSON.stringify()を実行して保存すると、diffはプロパティをさまざまな順序で一覧表示します。これは、diffおよびマージツールを使用してデータをさらにマージしようとすると厄介です。

理想的には、文字列化を実行する前に、または文字列化操作の一部として、オブジェクトのプロパティをアルファベット順に並べ替えたいと思います。多くの場所で配列オブジェクトを操作するためのコードがあり、これらを変更して常に明示的な順序でプロパティを作成することは困難です。

提案は大歓迎です!

凝縮された例:

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

これらの2つのstringify呼び出しの出力は異なり、データのdiffに表示されますが、アプリケーションはプロパティの順序を気にしません。オブジェクトは多くの方法と場所で構築されます。


文字列化しようとしているオブジェクト(JSON出力またはJSオブジェクトリテラルのいずれか)の例を教えてください
Bojangles

4
オブジェクト内のオブジェクトキーの順序が固定されているとは限りません。これは仕様によるものです。
通行人2013


1
@rab、どこで見つけたの?注:これは機能する可能性があります(おそらくほとんどの場合機能します)が、保証されているわけではありません。
Dogbert 2013

1
OPはこちら。ここではプロパティの順序に依存していませんでした。シリアル化されたデータの相違を回避する方法に関する質問だけです。これは最終的に、配列でプロパティを隠して、シリアル化の前にそれらを並べ替えることによって解決されました。
18

回答:


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
これは私が避けようとしていたことです:)しかし、重いですが、正しい解決策のようです。
2013

1
このコードにはバグがあります。は整数であるobj[i]ため、forループでは参照できませんi。また、プロパティ名は必ずしもそうではありません(したがって、この質問です)。それはあるはずですobj[arr[i]]
Andrew Ensley 2013年

1
コールバックは非同期動作に限定されません。
markyzm 2015

2
@Johannここでコールバックを使用することにより、彼はiterateObjectAlphabetically再利用可能な関数になります。コールバックがない場合、「ペイロードコード」は関数自体の中になければなりません。これは非常にエレガントなソリューションであり、多くのライブラリとJSコア自体で使用されているソリューションだと思います。例えばArray.forEach
Stijn de Witt

1
@marksyzm大丈夫です。私の場合、選択したフィールドのみを文字列化する必要がありました。したがって、あなたの答えは私を途中で置きました。ありがとう
Benj

39

あなたがJSON生成を制御しているなら(そしてあなたがそうであるように思えるなら)、あなたの目的のためにこれは良い解決策になるかもしれないと思います: json-stable-stringify

プロジェクトのウェブサイトから:

文字列化された結果から確定的ハッシュを取得するカスタムソートを使用した確定的JSON.stringify()

生成されたJSONが確定的である場合は、簡単にそれを比較/マージできるはずです。


リポの形が悪いことに注意してください。3年以内に更新されておらず、未解決の問題とプルリクエストがあります。新しい答えをいくつか見る方が良いと思います。
トム

3
@Tomまた、そのリポジトリには毎週500万(!)のダウンロードがあることに注意してください。つまり、積極的に利用されています。「未解決」の未解決の問題やプルリクエストがあることは、あまり意味がありません。作者がこれらの問題を気にしていない、または時間がないなどの理由だけです。すべてのライブラリを数か月ごとに更新する必要はありません。実際、imho、最高のライブラリは非常にまれに更新を取得します。何かが行われると、それが行われます。このプロジェクトに本当の問題はないと言っているわけではありませんが、もしそうなら、指摘してください。私はこのlib BTWには関与していません。
Stijn de Witt

ES6指定どおりに、オブジェクト(配列以外)をソートできるようになったと思います。ユーザーの編集に必要です。コンピュータに順序が関係なくても。それはそれを編集しているユーザーに行います。試す価値がありました。
TamusJRoyce

@Tomの発言に加えて、このプロジェクトには実行時の依存関係がないことも指摘しておきます。このリポジトリにセキュリティ/メンテナンスの問題がない場合、それはおそらく非常に安定していて安全に使用できます。また、ES6でテストしたところ、少なくともFirefoxでは正常に動作するようです。
フィル

27

プロパティ名のソートされた配列をの2番目の引数として渡すことができますJSON.stringify()

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

1
この動作に関する文書化された保証はありますか?
リッチレマー2017年

4
@richremerはい、ECMAScript標準では、の2番目のパラメーターJSON.stringify()が呼び出されreplacer、配列にすることができます。これは、を作成するためにPropertyList使用され、次にで使用されSerializeJSONObject()て、その順序でプロパティを追加します。これは、ECMAScriptの5以降では文書化されている
クリスチャン・ドールHeureuse

14
objにネストされたオブジェクトがある場合、ネストされたキーは2番目の引数にも存在する必要があることに注意してください。そうでなければ、それらはドロップされます!> JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' 反例:関連するすべてのキーのリストを作成する(ハッキーな)方法の1つは、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

Object.keys()は再帰的ではありません。これは、ネストされた配列またはオブジェクトを含むオブジェクトに対しては機能しません。
ジョー

25

すべてのキーを再帰的に取得するために、現在のベストアンサーの複雑さが必要な理由がわかりません。完璧なパフォーマンスが必要でない限りJSON.stringify()、最初はすべてのキーを取得し、2回目は実際に作業を行うために、2回だけ呼び出すことができるように思えます。そうすれば、再帰の複雑さはすべてによって処理され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を呼び出すab、キーとのみが取得され、は取得されませんcJSON.stringifyJSONシリアライゼーションプロセスで処理されるすべてのオブジェクトタイプの再帰についてすべて知っています。オブジェクトのようなものでも、配列でも(そしてすでに両方を認識するロジックが実装されているため)、すべてが正しく処理されます。
Jor

1
このソリューションが大好きで、とてもエレガントでフェイルセーフのようです。
Markus

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の簡潔なバージョン。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), {}));
}

構文が正しくありません。する必要があります-function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
ベン

これにより、現時点では、C#のDataContractJsonSerializerと「__type」がjson文字列の最初にリストされていない問題が修正されました。ありがとう。
ヨーグルトザワイズ

2
これは、Christianの回答と同様に、ネストされたオブジェクトでは正しく動作しません。
Daniel Griscom

sortObjPropertiesByKeyが定義されていません。sortObjByKeyを使用するつもりでしたか?
ポールリンチ

これは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%堅牢なソリューションが必要な場合は、独自の文字列化関数を作成する必要があります。

編集:

stringifyの非決定的な出力、特にブラウザの違いに関するDaffの詳細については、このSOの質問も参照してください。

JSONオブジェクトが変更されていないことを確定的に検証する方法は?


各オブジェクトのプロパティは既知であり(ほとんどの場合文字列)、自分のtoJSON文字列指定子でプロパティをハードコーディングすると、並べ替えよりもはるかに高速になる可能性があります...!
2013

以下の「orderedJsonStringify」はほとんど機能しました。しかし、これは私にとってより良い解決策のようです。C#DataContractJsonSerializerの '__type'をjson文字列の最初の項目にする必要がある場合。var json = JSON.stringify(myjsonobj、['__type'、 'id'、 'text'、 'somesubobj' etc .....]); すべてのキーをリストするのは少し苦痛です。
ヨーグルトザワイズ

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、2015

これに気づいていただきありがとうございます。OPの問題は、配列ではなくオブジェクトを再利用することでした。
Jason Parham 2015

@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年後半なので、言語内で利用できるオプションがいくつかあります。

更新:デビッドファーロングの答えは以前の試みに対する望ましいアプローチであると私は信じています。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);
}

これにより、サードパーティの依存関係なしで文字列化でき、さらにキーと値のエントリのペアでソートする独自のソートアルゴリズムを渡すことができるため、キー、ペイロード、またはその2つの組み合わせでソートできます。ネストされたオブジェクト、配列、および単純な古いデータ型の混合で機能します。

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

ありがとう、しかしどうやら順序は定義されておらず、たまたま機能するだけです。でも面白いアプローチ!
13

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

そして、それを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"}


1
うまく機能しますが、残念ながら再帰的ではありません。
Jonathan Gawrych

0

リスト内のオブジェクトに同じプロパティがない場合は、文字列化する前に結合マスターオブジェクトを生成します。

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が言ったように、いくつかの説明と概要を追加してください。よろしくお願いします。
ベンジ2017

この機能は私のプロジェクトからコピーされます。underscore.jsを使用しています。
聖人2017

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

誰もlodashのisEqual機能について言及していません。

2つの値を詳細に比較して、それらが等しいかどうかを判断します。

注:このメソッドは、配列、配列バッファー、ブール値、日付オブジェクト、エラーオブジェクト、マップ、数値、オブジェクトオブジェクト、正規表現、セット、文字列、シンボル、および型付き配列の比較をサポートしています。オブジェクトオブジェクトは、継承されていない列挙可能なプロパティによって比較されます。関数と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、それはキーが仕様によって作成時間によって順序付けられることが保証されていないので、それでも機能しません。
Dogbert 2013

次に、問題は、キーが固定された順序になるようにオブジェクトを文字列化する方法です。(obj内のキー)を実行して一時配列に格納し、それを手動で並べ替えることは不可能ではありません文字列を作成します。しかし、もっと簡単な方法があることを
切望してい

いいえ、それはほとんどそれを行う方法です。JavaScriptへようこそ。
marksyzm 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.