開発覚書はてな版

個人的な開発関連の備忘録

【TypeScript】Arrayをソートする拡張メソッドの実装 - その2

概要

下記の記事でArrayソート用の拡張メソッドを実装しました。

kakkoyakakko2.hatenablog.com

今回は、第1キーは昇順、第2キーは降順のようなケースに対応したいと思います。 以下の内容で実装していきたいと思います。

  • 引数にソートキーの他に昇順フラグを設ける。
  • 前回の実装についてはオーバーロードとして残す。

動作環境

  • TypeScript 2.9.x

サンプルソース

export {};

/** ソート情報 */
type SortInfo<K> = {
  /** ソートキー */
  sortKey: K,
  /** 昇順フラグ */
  asc: boolean
};

declare global {
  interface Array<T> {
    /**
     * 指定したプロパティを元に昇順にソートします。
     * @param sortKeys 昇順キー
     * @return ソート後の配列
     */
    orderBy<K extends keyof T>(...sortKeys: K[]): T[];

    /**
     * 指定したプロパティを元にソートします。
     * @param sortKeys sortKey:ソートキー, asc: 昇順フラグ
     * @return ソート後の配列
     */
    orderBy<K extends keyof T>(...sortKeys: { sortKey: K, asc: boolean }[]): T[];
  }
}

Array.prototype.orderBy = function<T, K extends keyof T>(...sortKeys: any[]): T[] {
  const items = this as T[];

  if (!Array.isArray(sortKeys) || sortKeys.length === 0)
    return items.sort();
  else {
    let sortInfos: SortInfo<K>[];
    if (typeof sortKeys[0] === 'object')
      sortInfos = sortKeys as SortInfo<K>[];
    else
      sortInfos = (<K[]>sortKeys).map(key => { return { sortKey: key, asc: true }; });
    return items.sort((a: T, b: T) => compare(a, b, sortInfos));
  }
};

/**
 * ソート判定処理
 * 再帰的にsortInfosを処理して、ソート判定を行います
 */
function compare<T, K extends keyof T>(value1: T, value2: T, sortInfos: SortInfo<K>[]): number {
  const info = sortInfos[0];
  const prop1 = value1[info.sortKey];
  const prop2 = value2[info.sortKey];

  if (prop1 !== prop2) {
    // 値が異なる場合は昇順フラグを元に大小判定
    if (info.asc)
      return (prop1 < prop2) ? -1 : 1;
    else
      return (prop1 > prop2) ? -1 : 1;
  } else {
    // 値が同じ場合はsortInfosが2つ以上残っている時は再帰処理
    if (sortInfos.length <= 1)
      return 0;
    else
      return compare(value1, value2, sortInfos.slice(1));
  }
}

SortInfoでソートキーと昇順フラグを管理するようにしています。

実行結果

const items = [ 
  { id: 2, name: 'bob' }, { id: 3, name: 'alex' }, { id: 1, name: 'char' }, { id: 4, name: 'bob' } 
];

const actual = items.orderBy({ sortKey: 'name', asc: true }, { sortKey: 'id', asc: false });
// output:  
// id: 3, name: 'alex'
// id: 4, name: 'bob'
// id: 2, name: 'bob'
// id: 1, name: 'char'

その他

TypeScriptの拡張メソッド関連の記事は以下を参照。

kakkoyakakko2.hatenablog.com

kakkoyakakko2.hatenablog.com