javascript sort 複数 - 文字列のプロパティ値でオブジェクトの配列をソートする



15 Answers

渡す値によってオブジェクトをソートする動的ソート機能を作成することもできます。

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        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"));

プロトタイプに追加する

(下の実装はMike Rのanswer触発されていanswer )

ネイティブオブジェクトプロトタイプを変更することはお勧めしませんが、独自のオブジェクトに実装できるように例を挙げるだけです (サポートする環境では、次のセクションに示すようにObject.definePropertyを使用することもできます。最後の部分で説明したように、列挙可能であるという負の副作用はありません)

プロトタイプの実装は、次のようなものになります( 実際の例です )。

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

プロトタイプに追加する「OK」の方法

IE v9.0以降をターゲットにしている場合は、前述のようにObject.definePropertyように使用します( 実際の例 )。

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

これは、 バインド演算子が到着するまで許容される妥協策です。

これらのプロトタイプの楽しみはすべてこれを可能にします:

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

あなたはこれを読むべきです

直接プロトタイプアクセスメソッド(Object.definePropertyは問題ありません)を使用し、他のコードがhasOwnPropertyチェックしない場合、子猫は死ぬ! さて、正直言って、どんな子猫にも本当に害はありませんが、おそらく事態が壊れ、チームの他のすべての開発者があなたを憎むでしょう:

最後の「SortBy」を参照してください。 うん。 クールではありません。 できる場合はObject.definePropertyを使用し、それ以外の場合はArray.prototypeをそのままにします。

降順 数値 アルゴリズム

私はJavaScriptオブジェクトの配列を持っています:

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

JavaScriptのlast_nomの値でソートするにはどうすればよいですか?

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




underscore.js

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

sortBy_.sortBy(list、iterator、[context])リストのソートされたコピーを返します。イテレータを使用して各値を実行した結果、昇順にランク付けされます。 Iteratorは、並べ替えるプロパティの文字列名(長さなど)でもかまいません。

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



姓が重複している場合は、名前順に並べ替えることができます。

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



カスタム比較関数を使用する代わりに、カスタムtoString()メソッド(デフォルトの比較関数によって呼び出される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();



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

単純なロジックすべてにフレームワークを追加するのではなく、十分にテストされたユーティリティフレームワークに頼って開発をスピードアップし、書かれたバグの量を減らすことは恥ずかしいことではありません。

Lodashはきれいなコードを生成し、より機能的なプログラミングスタイルを促進し、結果としてバグが少なくなります。 1つの垣間見れば、コードが何であるかが明確になります。

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使用し_.reverse

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

代わりにChainingを使用して両方を組み合わせたいですか?

const sortedObjs = _.chain(users).sortBy('user.age').reverse().value();



私はこの質問が古すぎることは知っていますが、私のような実装は見られませんでした。
このバージョンはSchwartzian変換イディオムに基づいています。

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: 'Pako',              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'));



オブジェクトの複雑な配列の(より多くの)並べ替え

おそらくこの配列のようなより複雑なデータ構造に遭遇するので、私はこのソリューションを拡張します。

TL; DR

@ege-Özcanの非常に素敵なanswer基づいたプラグイン可能なバージョンですか?

問題

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

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 、2 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://.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の使用




簡単な方法:

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

文字列を比較する際の誤りを防ぐには、 '.toLowerCase()'が必要であることを見てください。




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

デフォルトでは昇順にソートされます。




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

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

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




混乱を避けるために、小文字に変換する必要があります。

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

})



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



Ramdaを使用して、

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



元の例を考えてみましょう:

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)され普遍サポート戻る-1,0,1場合a<ba==ba>bそれぞれ。
  • ||最後の行のlast_nom優先順位が優先されfirst_nomます。
  • 減算は数値フィールドで機能します: var age_order = left.age - right.age;
  • 順序を逆にすることはできません。 return -last_nom_order || -first_nom_order || -age_order;



lodashまたはUnderscoreを使用して、そのケーキ

> const sortedList = _.orderBy(objs, [last_nom], [asc]); // asc or desc



Related