オブジェクトの配列を文字列プロパティ値で並べ替え


2763

JavaScriptオブジェクトの配列があります。

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

last_nomJavaScriptの値で並べ替えるにはどうすればよいですか?

についてsort(a,b)は知っていますが、それは文字列と数値に対してのみ機能するようです。toString()オブジェクトにメソッドを追加する必要がありますか?


7
:このスクリプトは、あなたがあなた自身の比較関数やソーター書きたい場合を除き、単にそれを行うことができますthomasfrank.se/sorting_things.html
Baversjo

最も簡単な方法は、ブラウザとノードの両方でネイティブに動作する同形ソート配列モジュールを使用して、あらゆるタイプの入力、計算フィールド、カスタムソート順をサポートすることです。
ロイド

回答:


3923

独自の比較関数を書くのは簡単です:

function compare( a, b ) {
  if ( a.last_nom < b.last_nom ){
    return -1;
  }
  if ( a.last_nom > b.last_nom ){
    return 1;
  }
  return 0;
}

objs.sort( compare );

またはインライン(c / o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 

441
またはインライン:objs.sort(function(a、b){return(a.last_nom> b.last_nom)?1:((b.last_nom> a.last_nom)?-1:0);});
Marco Demaio、2010


176
return a.last_nom.localeCompare(b.last_nom)うまくいきます。
Cerbrus 2013

146
フィールドが数値であるソートを探している人のために、比較関数本体:return a.value - b.value;(ASC)
Andre Figueiredo

20
@Cerbrus localeCompareは、外国語でアクセント付き文字を使用するときに重要であり、よりエレガントでもあります。
マルコスリマ

839

また、渡した値でオブジェクトをソートする動的ソート関数を作成することもできます。

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

したがって、次のようなオブジェクトの配列を持つことができます。

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

...そしてそれはあなたが行うときに機能します:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

実際、これはすでに質問に答えています。以下の部分は、多くの人が私に連絡して複数のパラメーターでは機能しないと不平を言っているために書かれています

複数のパラメーター

以下の関数を使用して、複数のソートパラメータを持つソート関数を生成できます。

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

これにより、次のようなことが可能になります。

People.sort(dynamicSortMultiple("Name", "-Surname"));

配列のサブクラス化

ネイティブオブジェクトを拡張できるES6を使用できる幸運な人のために:

class MyArray extends Array {
    sortBy(...args) {
        return this.sort(dynamicSortMultiple.apply(null, args));
    }
}

それはこれを可能にします:

MyArray.from(People).sortBy("Name", "-Surname");

JavaScriptのプロパティ名は任意の文字列にすることができ、 "-"で始まるプロパティがある場合(非常にありそうもない、おそらくお勧めできません)、別の何かを逆ソートとして使用するようにdynamicSort関数を変更する必要があります。インジケータ。
EgeÖzcan2013年

dynamicSort()上記の例では、小文字の前に大文字が配置されることに気づきました。私は値を持っている場合たとえば、APdAklin、およびAbe- ASCで結果をソートする必要がありますAbeAklinAPd。しかし、あなたの例で、結果はAPdAbeAklin。とにかくこの動作を修正するには?
ロイドバンク、

6
@LloydBanksこれを文字列に厳密に使用している場合は、のvar result = a[property].localeCompare(b[property]);代わりに使用できますvar result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
EgeÖzcan2017

1
@EgeÖzcanそれはどちらか、上部または下部に私が使用して終了しましたここでの実装のアイテムを置くべきpastebin.com/g4dt23jU
dzh

1
これは素晴らしい解決策ですが、数値を比較すると1つの問題があります。チェックの前にこれを追加してください:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan StefanStipićApr

416

ES6 / ES2015以降では、次の方法で行うことができます。

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

ES6 / ES2015より前

objs.sort(function(a, b) {
    return a.last_nom.localeCompare(b.last_nom)
});

29
これはJS 1.1以降で利用可能であり、これの重要な部分はES6 / 2015です。しかし、まだ非常に便利な、と私の意見では最良の答え
ジョン・ハーディング

13
@PratikKelwalkar:逆にする必要がある場合は、aとbの比較を入れ替えるだけです:objs.sort((a、b)=> b.last_nom.localeCompare(a.last_nom));
Vlad Bezden、2016年

last_nom配列の数値だけを使用する代わりに、インデックスを使用してフィールドをアドレス指定してソートすることは可能1ですか?
and-bri

4
@VladBezden回答ありがとうございます!このソリューションは、非常に小さなプログラムの努力と正しい並べ替えの結果、および["Name1"、 "Name10"、 "Name2"、 "something other"、 "Name11"]のような文字列配列を持つ最初のソリューションです。私は正しく動作するようにソートしましたobjs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
scipper

1
数値の降順でこれを行うには:.sort((a、b)=> b.numberProperty-a.numberProperty)。昇順:.sort((a、b)=> a.numberProperty-b.numberProperty)
セバスチャンパッテン

188

underscore.js

アンダースコアを使用し、その小さくて素晴らしい...

sortBy_.sortBy(list、iterator、[context])イテレータを介して各値を実行した結果によって昇順でランク付けされた、リストのソートされたコピーを返します。イテレータは、ソートするプロパティの文字列名(長さなど)にすることもできます。

var objs = [ 
  { first_nom: 'Lazslo',last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine'  },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortedObjs = _.sortBy( objs, 'first_nom' );

18
デビッド、答えを編集して言って var sortedObjs = _.sortBy( objs, 'first_nom' );。 この結果、それ自体はソートobjsされません。関数はソートされた配列を返します。それはそれをより明確にします。
Jess

10
並べ替えを逆にするには:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
G.

2
JavaScriptライブラリ「アンダースコア」をロードする必要があります:<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
と-BRI

5
でも利用可能 Lodashそれを好む方に
WoJ 2018

2
lodashではこれは同じです:var sortedObjs = _.sortBy( objs, 'first_nom' );または別の順序でそれを望む場合:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter

186

人々がそれをそんなに複雑にする理由を理解しないでください:

objs.sort(function(a, b){
  return a.last_nom > b.last_nom;
});

より厳密なエンジンの場合:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

演算子を入れ替えて、アルファベットの逆順に並べ替えます。


17
sortで使用される関数は-1、0、または1を返す必要があるため、これは実際には正しくありませんが、上記の関数はブール値を返します。並べ替えはChromeでは正常に機能しますが、たとえばPhantomJSでは失敗します。code.google.com/p/phantomjs/issues/detail?id=1090を
schup

いくつかのエンジンは愚かさを説明します、この方法は一種のそれを悪用しました。返信を適切なバージョンに更新しました。
p3lim 2014

19
編集して最初のバージョンを削除することをお勧めします。その簡潔さはより魅力的に見えますが、少なくとも確実には機能しません。誰かが1つのブラウザでそれを試してそれが機能する場合、彼らは問題があることに気付かないかもしれません(特に、彼らがコメントを読んでいない場合)。2番目のバージョンは適切に機能するため、最初のバージョンは実際には必要ありません。
ケイト

2
@Simon厳密な実装は解析と理解に数秒かかり、それがないとはるかに困難になるため、最初に「少し間違った」バージョンを最初に持っていることを本当に感謝しています。
アーロンシャーマン

1
@ Lion789はこれを行うだけです:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim

62

姓が重複している場合は、姓で並べ替えます。

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});

@BadFeelingAboutThis -1または1を返すとはどういう意味ですか?-1は構文的には文字通りAがBより小さいことを意味しますが、なぜ1または-1を使用するのですか 誰もがこれらの数値を戻り値として使用しているようですが、なぜですか?ありがとう。
Chris22、18年

1
@ Chris22負の数が返された場合は、配列のbaに来るはずです。正の数が返された場合は、のa後に来る必要があることを意味しbます。0が返された場合、それらは等しいと見なされます。:あなたは、常にドキュメント読むことができdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
BadFeelingAboutThis

@BadFeelingAboutThis説明とリンクをありがとう。信じられないかもしれませんが、1, 0, -1ここで質問する前に、を使用してさまざまなコードスニペットをグーグルで検索しました。必要な情報が見つかりませんでした。
Chris22、18年

48

プロトタイプ継承を使用して、この問題をシンプルかつ迅速に解決します。

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

例/使い方

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

更新:元の配列を変更しなくなりました。


5
別の配列を返すだけではありません。しかし、実際には元のものをソートします!。
Vinay Aggarwal

数値(つまり、0、1、2、10、11など)で自然な並べ替えを使用していることを確認する場合は、基数を設定してparseIntを使用します。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…so:return(parseInt(a [p]、10)> parseInt(b [p]、10))?1:(parseInt(a [p]、10)<parseInt(b [p]、10))?-1:0;
ポール、

@codehuntr修正していただきありがとうございます。しかし、私はこの感作を行うためにソート関数を作成する代わりに、データ型を修正するために別の関数を作成する方が良いと思います。ソート関数は、どのプロパティにどの種類のデータが含まれるかを判断できないためです。:)
Vinay Aggarwal

非常に素晴らしい。矢印を逆にしてasc / desc効果を取得します。
Abdul Sadik Yalcin 2017年

4
、これまでのコアオブジェクトの修正プロトタイプというソリューションを提案することはありません
pbanka

39

2018年現在、はるかに短くてエレガントなソリューションがあります。ただ使う。Array.prototype.sort()

例:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return a.value - b.value;
});

7
質問では、数値ではなく文字列を比較に使用しました。あなたの答えは数値によるソートには最適ですが、文字列による比較にはあまり適していません。
smcstewart

a.value - b.valueオブジェクトの属性(比較するために使用される番号、この場合には)データの様々な時間のために採用することができます。たとえば、正規表現を使用して、隣接する文字列の各ペアを比較できます
0leg

1
@ 0leg正規表現を使用してこの方法で文字列を比較する例をここに示したいと思います。
Bob Stein

この実装は、IDでソートする必要がある場合に非常に適しています。ええ、あなたは正規表現を使用して隣接する文字列を比較することを提案しましたが、これはソリューションをより複雑にしますが、この単純化されたバージョンの目的は、特定のソリューションと共に正規表現が使用される場合はそうではありません。シンプルさが最高です。
surendrapanday

33

正しくない古い答え:

arr.sort((a, b) => a.name > b.name)

更新

Beauchampのコメントから:

arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))

より読みやすい形式:

arr.sort((a, b) => {
  if (a.name < b.name) return -1
  return a.name > b.name ? 1 : 0
})

ネストされた三項なし:

arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))

説明:Number()キャストでしょうtrue1falseします0


それは機能しますが、結果は何らかの理由で不安定です
TitanFighter

@ AO17いいえ、それはしません。文字列を減算することはできません。
パトリックロバーツ

15
これでうまくいくはずです。arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
Jean-FrançoisBeauchamp

3
@Jean-FrançoisBeauchamp、あなたのソリューションは完全にうまく機能し、はるかに優れています。
アーサン

ナンバー付きの3つ目は簡単でいいです!
Kojo

29

カスタム比較関数を使用する代わりに、toString()(デフォルトの比較関数によって呼び出される)カスタムメソッドを使用してオブジェクトタイプを作成することもできます。

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();

29

Lodash.jsUnderscore.jsのスーパーセット)

ロジックの単純な部分ごとにフレームワークを追加するのは良いことではありませんが、十分にテストされたユーティリティフレームワークに依存することで、開発をスピードアップし、バグの量を減らすことができます。

Lodashは非常にクリーンなコードを生成し、より機能的なプログラミングスタイルを促進します。一見すると、コードの目的が何であるかが明らかになります。

OPの問題は、次のように簡単に解決できます。

const sortedObjs = _.sortBy(objs, 'last_nom');

より詳しい情報?たとえば、次のネストされたオブジェクトがあります。

const users = [
  { 'user': {'name':'fred', 'age': 48}},
  { 'user': {'name':'barney', 'age': 36 }},
  { 'user': {'name':'wilma'}},
  { 'user': {'name':'betty', 'age': 32}}
];

これで、_。property省略形user.ageを使用して、照合するプロパティへのパスを指定できます。ネストされたageプロパティでユーザーオブジェクトを並べ替えます。はい、ネストされたプロパティマッチングが可能です!

const sortedObjs = _.sortBy(users, ['user.age']);

逆にしたいですか?問題ない。_.reverseを使用します

const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));

チェーンを使用して両方を組み合わせたいですか?

const { chain } = require('lodash');
const sortedObjs = chain(users).sortBy('user.age').reverse().value();

または、チェーンよりもフローを好む場合

const { flow, reverse, sortBy } = require('lodash/fp');
const sortedObjs = flow([sortBy('user.age'), reverse])(users); 

28

使用できます

最も簡単な方法:ロダッシュ

https://lodash.com/docs/4.17.10#orderBy

このメソッドは_.sortByに似ていますが、ソートする反復子のソート順を指定できる点が異なります。順序が指定されていない場合、すべての値は昇順でソートされます。それ以外の場合、降順の場合は「desc」、対応する値の昇順の場合は「asc」の順序を指定します。

議論

コレクション(配列|オブジェクト):反復するコレクション。[iteratees = [_。identity]](Array [] | Function [] | Object [] | string []):ソートするために反復します。[orders](string []):イテレートのソート順。

戻り値

(配列):ソートされた新しい配列を返します。


var _ = require('lodash');
var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);

23

ここには多くの良い答えがありますが、より複雑な並べ替えを実現するために非常に単純に拡張できることを指摘しておきます。OR演算子を使用して、次のような比較関数をチェーンするだけです。

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

どこfn1fn2、、、 ...は[-1,0,1]を返すソート関数です。これにより、「fn1によるソート」、「fn2によるソート」が発生します。これは、SQLのORDER BYとほぼ同じです。

このソリューションは、trueに変換できる最初に評価された式に||評価される演算子の動作に基づいています

最も単純なフォームには、次のようなインライン関数が1つだけあります。

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

で2つのステップを実行すると last_nomfirst_nomソート順は次のようになります。

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

一般的な比較関数は次のようになります。

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

この関数は、数値フィールド、大文字と小文字の区別、任意のデータ型などをサポートするように拡張できます。

あなたはそれらをソート優先度によってそれらを連鎖させて使うことができます:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

ここでのポイントは、機能的アプローチを備えた純粋なJavaScriptは、外部ライブラリや複雑なコードなしで長い道のりを歩むことができるということです。文字列の解析を行う必要がないため、これも非常に効果的です。


23

使用例:

objs.sort(sortBy('last_nom'));

脚本:

/**
 * @description
 * Returns a function which will sort an
 * array of objects by the given key.
 *
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}
 */
const sortBy = (key, reverse) => {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};

これを分解してくれてありがとう1, 0, -1。数字が並べ替えに使用される理由を理解しようとしています。上記のあなたの説明でさえ、それはとてもよく見えます-私はまだそれを完全に理解していません。私は常に-1配列の長さのプロパティを使用するときと考えます。つまり arr.length = -1、アイテムが見つからないことを意味します。私はおそらくここで物事を混合していますが、数字1, 0, -1を使用して順序を決定する理由を理解していただけませんか?ありがとう。
Chris22、18年

1
これは完全に正確ではありませんが、次のように考えると役立つかもしれません。array.sortに渡される関数は、「a」という名前の引数として、配列内の項目ごとに1回呼び出されます。各関数呼び出しの戻り値は、項目「a」のインデックス(現在の位置番号)が次の項目「b」と比較してどのように変更されるかです。インデックスは配列の順序を決定します(0、1、2など)。したがって、「a」がインデックス5にあり、-1を返す場合、5 + -1 == 4(前に移動)5 + 0 == 5 (それがどこにあるかを保持します)など。最後に到達するまで、2つの近傍を比較するたびに配列をウォークし、ソートされた配列を残します。
ジェイミーメイソン

これをさらに説明する時間を割いていただきありがとうございます。だからあなたの説明とMDN Array.prototype.sortを使って、私がこれについて何を考えているのかを教えます:aandと比較してb、if aがより大きい場合はb、インデックスに1を加えaて後ろに配置しbaより小さい場合bから1を引いaて、の前に配置しbます。abが同じ場合は、0を追加してaそのままにします。
Chris22、18年

22

私は、ここで使用するような簡潔な比較方法を、私は両方のための作品ということですが、この特定のアプローチが提案見ていないstringnumber

const objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

const sortBy = fn => (a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

const getLastName = o => o.last_nom;
const sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

以下がその説明ですsortBy()

sortBy()fnオブジェクトからどの値を比較として使用するかを選択するを受け入れ、に直接渡すことができる関数を返しますArray.prototype.sort()。この例ではo.last_nom、比較の値としてを使用しているため、次のようArray.prototype.sort()な2つのオブジェクトを受け取るたびに

{ first_nom: 'Lazslo', last_nom: 'Jamf' }

そして

{ first_nom: 'Pig', last_nom: 'Bodine' }

を使用しております

(a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

それらを比較します。

を思い出してfn = o => o.last_nom、比較機能を同等のものに拡張できます

(a, b) => {
  const fa = a.last_nom;
  const fb = b.last_nom;
  return -(fa < fb) || +(fa > fb);
};

論理OR ||演算子には、ここで非常に役立つ短絡機能があります。それがどのように機能するかという理由で、上の関数の本体は

if (fa < fb) return -1;
if (fa > fb) return 1;
return 0;

追加のボーナスとして、ここに矢印機能のないECMAScript 5の同等のものがありますが、これは残念ながらより冗長です。

var objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortBy = function (fn) {
  return function (a, b) {
    var fa = fn(a);
    var fb = fn(b);
    return -(fa < fb) || +(fa > fb);
  };
};

var getLastName = function (o) { return o.last_nom };
var sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));


17

この質問が古すぎることはわかっていますが、私のような実装は見当たりませんでした。
このバージョンは、シュワルツ変換イディオムに基づいています。

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

使用方法の例を次に示します。

let games = [
  { name: 'Mashraki',          rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));

14

オブジェクトの複雑な配列のソート(詳細)

この配列のようなより複雑なデータ構造に遭遇する可能性があるので、解決策を拡張します。

TL; DR

@ege-Özcanの非常に美しい答えに基づいた、よりプラグ可能なバージョンです。

問題

以下に遭遇し、変更できませんでした。また、オブジェクトを一時的に平坦化したくありませんでした。また、主にパフォーマンス上の理由と自分で実装する楽しみのために、アンダースコア/ロダッシュを使用したくありませんでした。

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

ゴール

目標は、主に、次にPeople.Name.name副次的に並べ替えることです。People.Name.surname

障害物

現在、基本ソリューションでは、ブラケット表記を使用して動的にソートするプロパティを計算しています。ただし、ここでも、ブラケット表記を動的に構築する必要があります。People['Name.name']あります。

People['Name']['name']一方、単に行うことは静的であり、nを下に行くことしかできません番目のレベルを。

解決

ここでの主な追加は、オブジェクトツリーを下に移動して、指定する必要がある最後のリーフの値と中間リーフを決定することです。

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

People.sort(dynamicMultiSort(['Name','name'], ['Name', '-surname']));
// Results in...
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

// same logic as above, but strong deviation for dynamic properties 
function dynamicSort(properties) {
  var sortOrder = 1;
  // determine sort order by checking sign of last element of array
  if(properties[properties.length - 1][0] === "-") {
    sortOrder = -1;
    // Chop off sign
    properties[properties.length - 1] = properties[properties.length - 1].substr(1);
  }
  return function (a,b) {
    propertyOfA = recurseObjProp(a, properties)
    propertyOfB = recurseObjProp(b, properties)
    var result = (propertyOfA < propertyOfB) ? -1 : (propertyOfA > propertyOfB) ? 1 : 0;
    return result * sortOrder;
  };
}

/**
 * Takes an object and recurses down the tree to a target leaf and returns it value
 * @param  {Object} root - Object to be traversed.
 * @param  {Array} leafs - Array of downwards traversal. To access the value: {parent:{ child: 'value'}} -> ['parent','child']
 * @param  {Number} index - Must not be set, since it is implicit.
 * @return {String|Number}       The property, which is to be compared by sort.
 */
function recurseObjProp(root, leafs, index) {
  index ? index : index = 0
  var upper = root
  // walk down one level
  lower = upper[leafs[index]]
  // Check if last leaf has been hit by having gone one step too far.
  // If so, return result from last step.
  if (!lower) {
    return upper
  }
  // Else: recurse!
  index++
  // HINT: Bug was here, for not explicitly returning function
  // https://stackoverflow.com/a/17528613/3580261
  return recurseObjProp(lower, leafs, index)
}

/**
 * Multi-sort your array by a set of properties
 * @param {...Array} Arrays to access values in the form of: {parent:{ child: 'value'}} -> ['parent','child']
 * @return {Number} Number - number for sort algorithm
 */
function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments); // slight deviation to base

  return function (a, b) {
    var i = 0, result = 0, numberOfProperties = args.length;
    // REVIEW: slightly verbose; maybe no way around because of `.sort`-'s nature
    // Consider: `.forEach()`
    while(result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b);
      i++;
    }
    return result;
  }
}

JSBinの作業例


2
どうして?これは元の質問に対する答えではなく、「目標」はPeople.sort((a、b)=> {return a.Name.name.localeCompare(b.Name.name)|| a.Nameで簡単に解決できます.surname.localeCompare(b.Name.surname)})
Tero Tolonen

12

もう1つのオプション:

var someArray = [...];

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

someArray.sort(generateSortFn('name', true));

デフォルトでは昇順で並べ替えます。


1
必要に応じて、複数のフィールドでソートするため少し変更版はこちら:stackoverflow.com/questions/6913512/...
ラフシャンSamandarov

次のようになる可能性があります:エクスポート関数generateSortFn(prop:string、reverse:boolean = false):(... args:any)=> number {return(a、b)=> {return a [prop ] <b [prop]?逆行する ?1:-1:a [prop]> b [prop]?逆行する ?-1:1:0; }; }
デンカーニー

私は最短のコードよりも読みやすいコードを望みます。
ラフシャンサマンダロフ

同意しましたが、ユーティリティ関数を確認する必要がない場合もあります。
Den Kerny

11

プロパティでオブジェクトの配列をソートする単純な関数

function sortArray(array, property, direction) {
    direction = direction || 1;
    array.sort(function compare(a, b) {
        let comparison = 0;
        if (a[property] > b[property]) {
            comparison = 1 * direction;
        } else if (a[property] < b[property]) {
            comparison = -1 * direction;
        }
        return comparison;
    });
    return array; // Chainable
}

使用法:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc

このソリューションは、双方向の並べ替えに完全に機能しました。ありがとう
manjuvreddy

10

簡単な方法:

objs.sort(function(a,b) {
  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();
});

'.toLowerCase()'文字列の比較でエラーが発生するのを防ぐために必要であることを確認してください。


4
あなたは使用することができた機能を矢印もう少しエレガントなコードをできるように:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Sertage

これは、ここで説明されているのと同じ理由で間違っています
パトリックロバーツ

2
矢印関数はES5に値しません。まだたくさんのエンジンがES5に制限されています。私は(私の会社によって強制)ES5エンジン上だので、私の場合、私は、有意に優れ上記の答えを見つける
dylanh724

9

EgeÖzcanコードの追加のdescパラメータ

function dynamicSort(property, desc) {
    if (desc) {
        return function (a, b) {
            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
        }   
    }
    return function (a, b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}

9

Egeの動的ソリューションとVinayのアイデアを組み合わせると、優れた堅牢なソリューションが得られます。

Array.prototype.sortBy = function() {
    function _sortByAttr(attr) {
        var sortOrder = 1;
        if (attr[0] == "-") {
            sortOrder = -1;
            attr = attr.substr(1);
        }
        return function(a, b) {
            var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;
            return result * sortOrder;
        }
    }
    function _getSortFunc() {
        if (arguments.length == 0) {
            throw "Zero length arguments not allowed for Array.sortBy()";
        }
        var args = arguments;
        return function(a, b) {
            for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
                result = _sortByAttr(args[i])(a, b);
            }
            return result;
        }
    }
    return this.sort(_getSortFunc.apply(null, arguments));
}

使用法:

// Utility for printing objects
Array.prototype.print = function(title) {
    console.log("************************************************************************");
    console.log("**** "+title);
    console.log("************************************************************************");
    for (var i = 0; i < this.length; i++) {
        console.log("Name: "+this[i].FirstName, this[i].LastName, "Age: "+this[i].Age);
    }
}

// Setup sample data
var arrObj = [
    {FirstName: "Zach", LastName: "Emergency", Age: 35},
    {FirstName: "Nancy", LastName: "Nurse", Age: 27},
    {FirstName: "Ethel", LastName: "Emergency", Age: 42},
    {FirstName: "Nina", LastName: "Nurse", Age: 48},
    {FirstName: "Anthony", LastName: "Emergency", Age: 44},
    {FirstName: "Nina", LastName: "Nurse", Age: 32},
    {FirstName: "Ed", LastName: "Emergency", Age: 28},
    {FirstName: "Peter", LastName: "Physician", Age: 58},
    {FirstName: "Al", LastName: "Emergency", Age: 51},
    {FirstName: "Ruth", LastName: "Registration", Age: 62},
    {FirstName: "Ed", LastName: "Emergency", Age: 38},
    {FirstName: "Tammy", LastName: "Triage", Age: 29},
    {FirstName: "Alan", LastName: "Emergency", Age: 60},
    {FirstName: "Nina", LastName: "Nurse", Age: 54}
];

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");

1
アイデアをありがとう!ちなみに、配列プロトタイプを変更するように人々に勧めないでください(私の例の最後にある警告を参照してください)。
EgeÖzcan2013年

8

例によると、1つではなく2つのフィールド(姓、名)で並べ替える必要があります。Alasqlライブラリを使用して、この並べ替えを1行で行うことができます。

var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);

jsFiddleでこの例試してください。


8
objs.sort(function(a,b){return b.last_nom>a.last_nom})

1
実際にはうまくいかないようで、受け入れられた答えを使わなければなりませんでした。正しくソートされませんでした。
madprops 2017

8

元の例を考えると:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

複数のフィールドで並べ替え:

objs.sort(function(left, right) {
    var last_nom_order = left.last_nom.localeCompare(right.last_nom);
    var first_nom_order = left.first_nom.localeCompare(right.first_nom);
    return last_nom_order || first_nom_order;
});

ノート

  • a.localeCompare(b)普遍的にサポートされており、それぞれa<b、0 、-1を返します。a==ba>b
  • ||最後の行でがにlast_nom優先しfirst_nomます。
  • 減算は数値フィールドで機能します。 var age_order = left.age - right.age;
  • 逆の順序で否定、 return -last_nom_order || -first_nom_order || -age_order;

8

これを試して、

UPTO ES5

//Ascending Sort
items.sort(function (a, b) {
   return a.value - b.value;
});


//Descending Sort
items.sort(function (a, b) {
   return b.value - a.value;
});


IN ES6 & above:

// Ascending sort
items.sort((a, b) => a.value - b.value);

// Descending Sort
 items.sort((a, b) => b.value - a.value);

最良で簡単なソリューション
Omar Hasan

7

混乱を防ぐために、小文字に変換する必要がある場合があります。

objs.sort(function (a,b) {

var nameA=a.last_nom.toLowerCase(), nameB=b.last_nom.toLowerCase()

if (nameA < nameB)
  return -1;
if (nameA > nameB)
  return 1;
return 0;  //no sorting

})

7
function compare(propName) {
    return function(a,b) {
        if (a[propName] < b[propName])
            return -1;
        if (a[propName] > b[propName])
            return 1;
        return 0;
    };
}

objs.sort(compare("last_nom"));

1
投稿を編集して、コードの機能と問題を解決する理由を詳しく説明してください。通常、コードが含まれているだけの回答(たとえ機能していても)は、通常、OPが問題を理解するのに役立ちません。
ドレンミ

7

Ramdaを使用して、

npm install ramda

import R from 'ramda'
var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
var ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)
var descendingSortedObjs = R.reverse(ascendingSortedObjs)

6

これは単純な問題ですが、なぜ人々がそのような複雑なソリューションを持っているのかわかりません。
シンプルなソート関数(クイックソートアルゴリズムに基づく):

function sortObjectsArray(objectsArray, sortKey)
        {
            // Quick Sort:
            var retVal;

            if (1 < objectsArray.length)
            {
                var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // middle index
                var pivotItem = objectsArray[pivotIndex];                    // value in the middle index
                var less = [], more = [];

                objectsArray.splice(pivotIndex, 1);                          // remove the item in the pivot position
                objectsArray.forEach(function(value, index, array)
                {
                    value[sortKey] <= pivotItem[sortKey] ?                   // compare the 'sortKey' proiperty
                        less.push(value) :
                        more.push(value) ;
                });

                retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));
            }
            else
            {
                retVal = objectsArray;
            }

            return retVal;
        }

使用例:

var myArr = 
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];
myArr = sortObjectsArray(myArr, 'idx');

5
jsでクイックソートを実装するのは簡単なソリューションですか?単純なアルゴリズムですが、単純なソリューションではありません。
Andrew

外部ライブラリを使用せず、オブジェクトのプロトタイプを変更しないため、簡単です。私の意見では、コードの長さはコードの複雑さに直接影響しません
Gil Epshtain 2015年

2
さて、私は別の言葉で試してみましょう:ホイールを再発明することは簡単な解決策ですか?
Roberto14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.