TypeScript列挙型からオブジェクト配列へ


109

私はこのように定義された列挙型を持っています:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

ただし、以下のようにAPIからオブジェクト配列/リストとして表現したいと思います。

[{id: 1, name: 'Percentage'}, 
 {id: 2, name: 'Numeric Target'},
 {id: 3, name: 'Completed Tasks'},
 {id: 4, name: 'Average Milestone Progress'},
 {id: 5, name: 'Not Measured'}]

これを行う簡単でネイティブな方法はありますか、それとも列挙型をintとstringの両方にキャストする関数を作成し、オブジェクトを配列に作成する必要がありますか?


列挙型は、実行時に存在する実際のオブジェクトです。したがって、次のような方法でマッピングを逆にすることができますGoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]。列挙型の名前を取得します。それが役立つかどうかはわかりません。
Diullei 2017年

「APIから」についてより適切な説明をしていただけますか。使用例を
教えてください

回答:


52

トリッキーな点は、TypeScriptが発行されたオブジェクトの列挙型を「double」マップするため、キーと値の両方でアクセスできることです。

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

として放出されます

{
   Part1: 0,
   Part2: 1,
   0: 'Part1',
   1: 'Part2'
}

したがって、マッピングする前に、まずオブジェクトをフィルタリングする必要があります。したがって、@ Diulleiのソリューションには正しい答えがあります。これが私の実装です:

// Helper
const StringIsNumber = value => isNaN(Number(value)) === false;

// Turn enum into array
function ToArray(enumme) {
    return Object.keys(enumme)
        .filter(StringIsNumber)
        .map(key => enumme[key]);
}

次のように使用します。

export enum GoalProgressMeasurements {
    Percentage,
    Numeric_Target,
    Completed_Tasks,
    Average_Milestone_Progress,
    Not_Measured
}

console.log(ToArray(GoalProgressMeasurements));

1
場合MMMenum MyEnum { Part1 = 0, Part2 = 1 }になり { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } 、その後、なぜときconsole.log(Object.values(MyEnum))、それはちょうどだけ0,1を出力しますか?
フアン・ホセ・ラミレス

@JuanJoséRamírezどこにあると思いますか?私にとっては次のようにObject.values(MyEnum)評価されます["Part1", "Part2", 0, 1]
user83 6320

console.log(Object.values(MyEnum))コンポーネントに印刷しました。私はAngularを使用していますが、それが関連しているかどうかはわかりません。私は活字体で経験していることはないよ
フアン・ホセ・ラミレス

TSのバージョンが異なると動作が変わる可能性がありますか?
フアン・ホセ・ラミレス

3
docstypescriptlang.org/docs/handbook/release-notes/…をチェックしてきましたが、文字列列挙型の動作が異なるようです。逆マッピングはまったく生成されません。私のコードでは、この例の文字列ではなく、文字列列挙型を使用していました。
フアン・ホセ・ラミレス

45

ES8を使用している場合

この場合のみ、完全に正常に機能します。指定された列挙型の値配列が表示されます。

enum Colors {
  WHITE = 0,
  BLACK = 1,
  BLUE = 3
}

const colorValueArray = Object.values(Colors); //[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]

あなたはcolorValueArrayこのようになります[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]。すべてのキーは配列の前半にあり、すべての値は後半にあります。

この種の列挙型でも問題なく動作します

enum Operation {
    READ,
    WRITE,
    EXECUTE
}

しかし、このソリューションは、このような異種列挙型では機能しません

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

9
これにより重複が発生することに注意してください。各要素の文字列値と数値。(string | YourEnumType)[]これは、それぞれの場合に必要なタイプではありません。
ChristianIvicevic19年

前半がキーになり、後半が値になることが保証されていますか?参照はありますか?
ニーキー

1
Object.values()ES6の一部ではありません。ES2017の一部です。
atiyar

37

列挙型は、実行時に存在する実際のオブジェクトです。したがって、次のような方法でマッピングを逆にすることができます。

let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured

これに基づいて、次のコードを使用できます。

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

let map: {id: number; name: string}[] = [];

for(var n in GoalProgressMeasurements) {
    if (typeof GoalProgressMeasurements[n] === 'number') {
        map.push({id: <any>GoalProgressMeasurements[n], name: n});
    }
}

console.log(map);

参照:https//www.typescriptlang.org/docs/handbook/enums.html


3
= 2までデフォルトを書き込む必要はありません= 5-それ以降= 1は自動的に+1になります。
sebilasse 2017年

9
たぶんあなたはそうする必要はありませんが、それはより表現力豊かです。それは私見をより良くします。
SubliemeSiem

5
これは文字列値の列挙型では機能しないことに注意してください
Bashar AliLabadi19年

21

簡単な解決策。次の関数を使用して、列挙型をオブジェクトの配列に変換できます。

 buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
 }

そのアンダースコアを取り除く必要がある場合は、次のように正規表現を使用できます。

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }

8
タイプ番号のキーをフィルタリングする必要があります Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Salvador Rubio Martinez

13

私が使う

Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));

仕事をする単純な1行。

それは3つの簡単なステップで仕事をします
-を使用してキーと値の組み合わせをロードしObject.entriesます。
-非数値を除外します(typescriptが逆ルックアップの値を生成するため)。
-次に、それを好きな配列オブジェクトにマップします。


フロントエンドのIEと互換性がありません(サポートすべきではありません。つまり、すばらしい答えです...しかし、私は顧客だと思います)。バベルはそれを蒸散期待して、私はそれを検証していないので、他のアプローチにこだわる
TamusJRoyceを

文字列列挙型は簡単です。実行するだけですObject.values(GoalProgressMeasurement)
CMS 2010


10
class EnumHelpers {

    static getNamesAndValues<T extends number>(e: any) {
        return EnumHelpers.getNames(e).map(n => ({ name: n, value: e[n] as T }));
    }

    static getNames(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'string') as string[];
    }

    static getValues<T extends number>(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'number') as T[];
    }

    static getSelectList<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        const selectList = new Map<T, string>();
        this.getValues(e).forEach(val => selectList.set(val as T, stringConverter(val as unknown as U)));
        return selectList;
    }

    static getSelectListAsArray<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        return Array.from(this.getSelectList(e, stringConverter), value => ({ value: value[0] as T, presentation: value[1] }));
    }

    private static getObjValues(e: any): (number | string)[] {
        return Object.keys(e).map(k => e[k]);
    }
}

1
これらのヘルパーに感謝します。非常に便利。
user9065 7320年

4

まず、この列挙型のキーの配列を取得します。次に、map()関数を使用して、データを目的の形式に変換します。idはキーから取得され、nameは同じキーによってenumから取得されます。

const converted = Object.keys(GoalProgressMeasurements).map(key => {
        return {
            id: GoalProgressMeasurements[key],
            name: key,
        };
    });

2
stackoverflowへようこそ。質問に答えるときは、コードスニペットの機能を説明することをお勧めします。詳細については、こちらを参照してください。回答する方法
Djensen


回答に説明や詳細を追加することを検討してください。質問に答えるかもしれませんが、答えとしてコードを追加するだけでは、OPや将来のコミュニティメンバーが問題や提案された解決策を理解するのに役立つとは言えません。
マキシム

2

TypeScript列挙型の値になり得る文字列/数値の混合を正しく処理するものがないため、上記の回答はどれも好きではありませんでした。

次の関数は、TypeScript列挙型のセマンティクスに従って、キーから値への適切なマップを提供します。そこから、オブジェクトの配列、キーのみ、または値のみを取得するのは簡単です。

/**
 * Converts the given enum to a map of the keys to the values.
 * @param enumeration The enum to convert to a map.
 */
function enumToMap(enumeration: any): Map<string, string | number> {
  const map = new Map<string, string | number>();
  for (let key in enumeration) {
      //TypeScript does not allow enum keys to be numeric
      if (!isNaN(Number(key))) continue;

      const val = enumeration[key] as string | number;

      //TypeScript does not allow enum value to be null or undefined
      if (val !== undefined && val !== null)
          map.set(key, val);
  }

  return map;
}

使用例:

enum Dog {
    Rover = 1,
    Lassie = "Collie",
    Fido = 3,
    Cody = "Mutt",
}

let map = enumToMap(Dog); //Map of keys to values

lets objs = Array.from(map.entries()).map(m => ({id: m[1], name: m[0]})); //Objects as asked for in OP
let entries = Array.from(map.entries()); //Array of each entry
let keys = Array.from(map.keys()); //An array of keys
let values = Array.from(map.values()); //An array of values

また、OPが列挙型を逆方向に考えていることも指摘しておきます。列挙型の「キー」は技術的には左側にあり、値は右側にあります。TypeScriptを使用すると、RHSの値を好きなだけ繰り返すことができます。


1

enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}
    
const array = []
    
for (const [key, value] of Object.entries(GoalProgressMeasurements)) {
    if (!Number.isNaN(Number(key))) {
        continue;
    }

    array.push({ id: value, name: key.replace('_', '') });
}

console.log(array);


コードを貼り付けるだけでなく、常にコンテキストに答えを入れてください。詳細については、こちらをご覧ください。
gehbiszumeis

1

簡単な解決策があります。それを実行するObject.keys(Enum)と、最初のスライスの値と2番目のキーに値とキーの配列が表示されます。2番目のスライスを返さない理由は次のとおりです。 。

enum Enum {
   ONE,
   TWO,
   THREE,
   FOUR,
   FIVE,
   SIX,
   SEVEN
}
const keys = Object.keys(Enum); 
console.log(keys.slice(keys.length / 2));

なぜこれが反対票を投じられたのか興味がありますか?私も同じ考えを持っていました。TypeScriptが変更され、同じ方法で列挙型が作成されなくなるのではないかと心配しますか?
トニースミス

0

あなたはこの方法でそれを行うことができます:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

export class GoalProgressMeasurement {
    constructor(public goalProgressMeasurement: GoalProgressMeasurements, public name: string) {
    }
}

export var goalProgressMeasurements: { [key: number]: GoalProgressMeasurement } = {
    1: new GoalProgressMeasurement(GoalProgressMeasurements.Percentage, "Percentage"),
    2: new GoalProgressMeasurement(GoalProgressMeasurements.Numeric_Target, "Numeric Target"),
    3: new GoalProgressMeasurement(GoalProgressMeasurements.Completed_Tasks, "Completed Tasks"),
    4: new GoalProgressMeasurement(GoalProgressMeasurements.Average_Milestone_Progress, "Average Milestone Progress"),
    5: new GoalProgressMeasurement(GoalProgressMeasurements.Not_Measured, "Not Measured"),
}

そして、あなたはそれをこのように使うことができます:

var gpm: GoalProgressMeasurement = goalProgressMeasurements[GoalProgressMeasurements.Percentage];
var gpmName: string = gpm.name;

var myProgressId: number = 1; // the value can come out of drop down selected value or from back-end , so you can imagine the way of using
var gpm2: GoalProgressMeasurement = goalProgressMeasurements[myProgressId];
var gpmName: string = gpm.name;

必要に応じて、オブジェクトの追加プロパティを使用してGoalProgressMeasurementを拡張できます。私は、値以上を含むオブジェクトである必要があるすべての列挙にこのアプローチを使用しています。


0

文字列値を持つ列挙型は数値を持つ列挙型とは異なるため、@ user8363ソリューションから非数値をフィルタリングすることをお勧めします。

列挙型の文字列、混合数から値を取得する方法は次のとおりです。

    //Helper
    export const StringIsNotNumber = value => isNaN(Number(value)) === true;
    
    // Turn enum into array
    export function enumToArray(enumme) {
      return Object.keys(enumme)
       .filter(StringIsNotNumber)
       .map(key => enumme[key]);
    }


0

TypeScriptスレッドで、タイピングがサポートされている有効なTypeScript関数を誰も提供していないことに驚いています。@ user8363ソリューションのバリエーションは次のとおりです。

const isStringNumber = (value: string) => isNaN(Number(value)) === false;

function enumToArray<T extends {}>(givenEnum: T) {
  return (Object.keys(givenEnum).filter(isStringNumber) as (keyof T)[]).map(
    (key) => givenEnum[key]
  );
}

0

順序が保証されるとは思いません。そうでなければ、Object.entries結果の後半をスライスしてそこからマップするのは簡単です。

上記の回答に関する唯一の(非常にマイナーな)問題は、

  • 文字列と数値の間には不必要な型変換がたくさんあります。
  • 1回の反復が同じようにクリーンで効果的である場合、エントリは2回反復されます。
type StandardEnum = { [id: string]: number | string; [nu: number]: string;}

function enumToList<T extends StandardEnum> (enm: T) : { id: number; description: string }[] {
    return Object.entries(enm).reduce((accum, kv) => {
        if (typeof kv[1] === 'number') {
            accum.push({ id: kv[1], description: kv[0] })
        }
        return accum
    }, []) // if enum is huge, perhaps pre-allocate with new Array(entries.length / 2), however then push won't work, so tracking an index would also be required
}

0

これが私が使っている正しいタイピングの簡単な関数です

/**
 * Helper to produce an array of enum values.
 * @param enumeration Enumeration object.
 */
export function enumToArray<T, G extends keyof T = keyof T>(enumeration: T): T[G][] {
  // tslint:disable: comment-format

  // enum Colors {
  //   WHITE = 0,
  //   BLACK = 1,
  // }
  // Object.values(Colors) will produce ['WHITE', 'BLACK', 0, 1]

  // So, simply slice the second half
  const enumValues = Object.values(enumeration);
  return enumValues.slice(enumValues.length / 2, enumValues.length) as T[G][];
}

使用例:

enum Colors {
  Red = 1,
  Blue = 2,
}
enumToArray(Colors)

0
function enumKeys(_enum) {
  const entries = Object.entries(_enum).filter(e => !isNaN(Number(e[0])));
  if (!entries.length) {
    // enum has string values so we can use Object.keys
    return Object.keys(_enum);
  }
  return entries.map(e => e[1]);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.