1
0

More than 3 years have passed since last update.

JavaScriptのObjectの要素をキーにしてソートしたい時の話

Last updated at Posted at 2020-08-27

はじめに

JavaScriptでObjectのキーをその要素でソートしたいときに詰まったことのまとめです.
特に,Objectの要素にNaNが含まれていたり,要素が存在しない場合の対処法です.

問題

const data = {
  "1": {"p1": 0.2, "p2": 1, "p3": 0},
  "2": {"p1": 0.7, "p2": "-", "p3": 5},
  "3": {"p1": 0.3, "p2": -1},
  "4": {"p1": 0.1, "p2": 3, "p3": 1}
};

こんなObjectがあったときにObject.keys(x)を要素(すなわちp1, p2, p3)の値をキーとしてソートしたい.
Array.prototype.sort

特に問題のないケース

上の例でp1でソートしたいとき.data[k]["p1"]が全て存在し,全て数値であるような場合は簡単.

let arr = Object.keys(data);
console.log(arr); // => ["1", "2", "3", "4"]
const cmp = (k1, k2) => {
  return data[k1]["p1"] - data[k2]["p1"];
}
arr.sort(cmp);
console.log(arr); // => ["4", "1", "3", "2"]

compareFunctionでObjectの値を参照すればOK.

要素にNaNが含まれているとき

上の例でp2でソートしたいとき.data[k]["p1"]が全て存在するが,NaNが混ざっている場合(isNaN(data["2"]["p2"])===true).
そのままやると...

let arr = Object.keys(data);
console.log(arr); // => ["1", "2", "3", "4"]
arr.sort((k1, k2) => data[k1]["p2"] - data[k2]["p2"]);
console.log(arr); // => ["1", "2", "3", "4"]

:middle_finger_tone2::poop::middle_finger_tone2:
NaNに対応する必要がある.NaNの要素が一番うしろにソートされるように書いてみる.

let arr = Object.keys(data);
console.log(arr); // => ["1", "2", "3", "4"]
const cmp2 = (k1, k2) => {
  const [t1, t2] = [isNaN(data[k1]["p2"]), isNaN(data[k2]["p2"])];
  if (t1 && t2) // case 1
    return 0;
  else if (t1 || t2) // case 2
    return t1 ? 1 : -1;
  else // case 3
    return data[k1]["p2"] - data[k2]["p2"];
};
arr.sort(cmp2);
console.log(arr); // => ["3", "1", "4", "2"]
  • case 1: 比較される2つの要素が両方NaNのとき.2つは等しいので0を返す.
  • case 2: どちらか一方がNaNのとき.return t1 ? 1 : -1;data[k1]["p2]NaNであるとき,k1を後ろに起きたいので,1を返す.
  • case 3: 2つとも数値のとき.これははじめの場合と同じ.

要素が存在しない場合

上の例でp3でソートしたいとき.data["3"]["p3"]が存在しない.このときisNaN(data["3"]["p3"])===trueとなるので,NaNの場合と同じように扱える.
よってこの場合は簡単

一般化してみる

オブジェクトとキーを渡してソートされた配列を返す関数を書いてみる.

const sortObject = (obj, sortKey, reverse=false) => {
  const sign = reverse ? -1 : 1;
  const cmp = (k1, k2) => {
    const [t1, t2] = [isNaN(data[k1][sortKey]), isNaN(data[k2][sortKey])];
    if (t1 && t2)
      return 0;
    else if (t1 || t2)
      return t1 ? 1 : -1;
    else
      return sign * (data[k1][sortKey] - data[k2][sortKey]);
  };
  return Object.keys(obj).slice().sort(cmp);
};

console.log(sortObject(data, "p1")); // => ["1", "2", "3", "4"]
console.log(sortObject(data, "p2")); // => ["3", "1", "4", "2"]
console.log(sortObject(data, "p3")); // => ["1", "4", "2", "3"]

ただし,これはNaNと非存在要素の比較はしてない.つまり,それらの順番は多分保証されない(知らんけど).

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0