ymmr
Recent Articles
    公開日: 2023/10/21

    String enum をソートキーとして使う

    Prisma で定義したモデルがプロパティとして enum を持っていたとします。それをソートキーとして使用したい場面がありました。この記事では emum をキーとして、配列のオブジェクト群を Array.prototype.sort で並び替える方法を紹介します。

    スキーマ定義

    スキーマ内で以下のようなレベル (enum) とメンバー (model) を定義したものとします。

    /prisma/schema.prisma
    generator client {
      provider = "prisma-client-js"
    }
    
    // Enum は MySQL または PostgreSQL でのみサポートされる。
    datasource db {
      provider = "postgresql"
      url      = env("DATABASE_URL")
    }
    
    // Enum で 4 段階のレベル (入門、初級、中級、上級) を定義する。
    enum Level {
      BEGINNER
      ELEMENTARY
      INTERMEDIATE
      ADVANCED
    }
    
    // レベルをフィールドに持つメンバーを定義する。
    model Member {
      id          Int      @id @default(autoincrement())
      createdAt   DateTime @default(now()) @map("created_at")
      updatedAt   DateTime @updatedAt @map("updated_at")
      name        String
      level       Level    @default(BEGINNER)
      //          ^^^^^^ レベルを参照する。
    
      @@map("members")
    }
    

    実際のコード

    prisma generate を実行すると、prisma/client 以下に Level というオブジェクトとその型定義が生成されます。

    /node_modules/.prisma/client/index.d.ts
    export const Level: {
      BEGINNER: 'BEGINNER',
      ELEMENTARY: 'ELEMENTARY',
      INTERMEDIATE: 'INTERMEDIATE',
      ADVANCED: 'ADVANCED'
    };
    
    export type Level = (typeof Level)[keyof typeof Level]
    

    sort 関数は並び順を決めるコールバック関数 (compareFn) を引数として受け取ることができます。compareFn は配列の要素を 2 つ受け取り、その返り値によって要素を並び替えるのですが、オブジェクトのままでは意図した通りに比較できません。

    ここで比較可能なインデックスを持つ配列に変換することがポイントとなります。今回はレベルの昇順 (入門 > 初級 > 中級 > 上級) に並び替えたいので、以下のようなコードを書きました。

    import { $Enums } from '@prisma/client';
    
    import type { Member } from '@prisma/client';
    
    export const sortMembersByLevel = (members: Member[]): Member[] => {
      // 並び替えたい順に enum 値を配列に格納する。
      // 今回のケースではスキーマ定義の並び順をそのまま使えるため、enum の値を配列として取り出すだけでよい。
      const levelOrder = Object.values($Enums.Level);
    
      // 並び順を調整したい場合は、任意の順番で配列をつくる。
      // const levelOrder = [
      //   $Enums.Level.ADVANCED,
      //   $Enums.Level.INTERMEDIATE,
      //   $Enums.Level.ELEMENTARY,
      //   $Enums.Level.BEGINNER,
      // ] as const;
    
      return members.sort(
        (a, b) => levelOrder.indexOf(a['level']) - levelOrder.indexOf(b['level']),
      );
    };
    

    参考

    Typescript Sorting based on Enum constantsIs it possible to have a list of enums sorted by the order these enums were declared? enum MyEnum { VALUE_1, VALUE_3, VALUE_2 } I create a list in a random order let list = [MyEnum.V...stackoverflow.com