javascript - TypeScript-enum objekti-taulukoksi


Minulla on enum määritelty tällä tavalla:

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

Kuitenkin haluaisin, että se esitetään objektitaulukko/listana API-stamme seuraavasti:

[{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'}]

Onko tähän olemassa helppoa ja luontevaa tapaa vai täytyykö minun rakentaa funktio, joka muuntaa enumin sekä intiksi että stringiksi, ja rakentaa objektit taulukkoon?

Vastaus #1

Jos käytät ES8:aa

Tässä tapauksessa se toimii täydellisesti. Se antaa sinulle arvojen taulukon annetusta enum-tyypistä.

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

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

Saat colorValueArray tältä näyttävänä: [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Kaikki avaimet ovat taulukon ensimmäisessä puoliskossa ja kaikki arvot toisessa puoliskossa.

Myös tämän tyyppinen enum toimii hyvin

enum Operation {
    READ,
    WRITE,
    EXECUTE
}

Mutta tämä ratkaisu ei toimi heterogeenisten enumien kanssa kuten tämä

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

Vastaus #2

Hankala osa on se, että TypeScript 'kaksoiskarttaa' enumin lähetetyssä objektissa, joten siihen voidaan päästä käsiksi sekä avaimella että arvolla.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

vapautetaan

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

Joten sinun pitäisi suodattaa objekti ensin ennen kartoitusta. Joten @Diullei:n ratkaisu on oikea. Tässä on minun toteutukseni:

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

Käytä sitä näin:

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

console.log(ToArray(GoalProgressMeasurements));

Vastaus #3

Yksinkertaisesti tämä palauttaa taulukon enum-arvoista:

 Object.values(myEnum);

Vastaus #4

Enumit ovat oikeita objekteja, jotka ovat olemassa suoritusaikana. Joten voit kääntää kohdistuksen tekemällä jotain tällaista:

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

Tämän perusteella voit käyttää seuraavaa koodia:

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

Viite: https://www.typescriptlang.org/docs/handbook/enums.html

Vastaus #5

Helppo ratkaisu. Voit käyttää seuraavaa funktiota muuntaaksesi Enumisi taulukoksi objekteja.

 buildGoalProgressMeasurementsArray(): Object[] {

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

Jos sinun pitäisi poistaa tuo alaviiva, voisimme käyttää regexiä seuraavasti:

buildGoalProgressMeasurementsArray(): Object[] {

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

Vastaus #6

Minä käytän

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

Yksinkertainen yksi rivi, joka hoitaa työn.

Se suorittaa tehtävän 3 yksinkertaisessa vaiheessa
- Lataa avain- ja arvoyhdistelmät käyttäen Object.entries.
- Suodattaa pois ei-numerot (koska TypeScript luo arvot käänteishakua varten).
- Sitten kartoitamme sen haluamaamme taulukko-objektiin.

Vastaus #7

Kiitos polkovnikov.ph:lle, pystyin vihdoin löytämään ratkaisun, joka toimii useimmissa käyttötapauksissa.

Kelpaava ratkaisu kysymykseen

type Descripted<T> = {
    [K in keyof T]: {
        readonly id: T[K];
        readonly description: string;
    }
}[keyof T]

/**
 * Helper to produce an array of enum descriptors.
 * @param enumeration Enumeration object.
 * @param separatorRegex Regex that would catch the separator in your enum key.
 */
export function enumToDescriptedArray<T>(enumeration: T, separatorRegex: RegExp = /_/g): Descripted<T>[] {
    return (Object.keys(enumeration) as Array<keyof T>)
        .filter(key => isNaN(Number(key)))
        .filter(key => typeof enumeration[key] === "number" || typeof enumeration[key] === "string")
        .map(key => ({
            id: enumeration[key],
            description: String(key).replace(separatorRegex, ' '),
        }));
}

Esimerkki :

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

console.log(enumToDescriptedArray(GoalProgressMeasurements))
// Produces:
/*
[
    {id: 1, description: "Percentage"},
    {id: 2, description: "Numeric Target"},
    {id: 3, description: "Completed Tasks"},
    {id: 4, description: "Average Milestone Progress"},
    {id: 5, description: "Not Measured"}
]
*/

Myös on hyödyllinen util-funktio, jota käytän karttaakseni enumerointi-objektin sen saatavilla olevien arvojen taulukkoon:

Kartoitin

type NonFunctional<T> = T extends Function ? never : T;

/**
 * Helper to produce an array of enum values.
 * @param enumeration Enumeration object.
 */
export function enumToArray<T>(enumeration: T): NonFunctional<T[keyof T]>[] {
    return Object.keys(enumeration)
        .filter(key => isNaN(Number(key)))
        .map(key => enumeration[key])
        .filter(val => typeof val === "number" || typeof val === "string");
}

Työskentelevät käyttötapaukset

  • Numeerinen luettelo
enum Colors1 {
    WHITE = 0,
    BLACK = 1
}
console.log(Object.values(Colors1)); // ['WHITE', 'BLACK', 0, 1]
console.log(enumToArray(Colors1));   // [0, 1]
  • Merkkijonoenum
enum Colors2 {
    WHITE = "white",
    BLACK = "black"
}
console.log(Object.values(Colors2)); // ['white', 'black']
console.log(enumToArray(Colors2));   // ['white', 'black']
  • Eriytetty luettelo
enum Colors4 {
    WHITE = "white",
    BLACK = 0
}
console.log(Object.values(Colors4)); // ["BLACK", "white", 0]
console.log(enumToArray(Colors4));   // ["white", 0]
  • Lueteltu tyyppi yhdistettynä nimialueeseen, jossa on vietyjä funktioita
enum Colors3 {
    WHITE = "white",
    BLACK = "black"
}
namespace Colors3 {
    export function fun() {}
}
console.log(Object.values(Colors3)); // ['white', 'black', Function]
console.log(enumToArray(Colors3));   // ['white', 'black']

Vastaus #8

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

Vastaus #9

Esimerkki enum-arvon saamisesta taulukon sisältä:

export enum DocumentationTypeEnum {
  GDPR = 'GDPR',
  HELP = 'HELP',
  OTHER = 'OTHER',
  FOOTER = 'FOOTER'
}
const keys = Object.keys(DocumentationTypeEnum);
    
console.log(keys); // Output :  ["GDPR", "HELP", "OTHER", "FOOTER"]

Vastaus #10

En pitänyt mistään yllä olevista vastauksista, koska mikään niistä ei käsittele oikein merkkijonojen ja numeroiden sekoitusta, jotka voivat olla arvoja TypeScript-enumeissa.

Seuraava funktio noudattaa TypeScript-enumien semantiikkaa tarjotakseen oikean Mapin avaimista arvoihin. Siitä eteenpäin on helppoa saada taulukko objekteista tai vain avaimista tai vain arvoista.

/**
 * 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;
}

Esimerkin käyttö:

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

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

let 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

Huomautan myös, että alkuperäinen kirjoittaja ajattelee luetteloita väärinpäin. Luettelon "avain" on teknisesti vasemmalla puolella ja arvo oikealla puolella. TypeScript sallii toistaa arvoja oikealla puolella niin paljon kuin haluat.

Aiheeseen liittyviä artikkeleita