繁体   English   中英

循环对象数组和格式值

[英]Loop over an array of objects and format values

在我的 API 数据响应中,我试图遍历items中的对象数组。 我需要格式化 object 中的每个值。 对象items列表中的每个键对应于我的header键中的格式类型。 我还有一个getFormat function 来帮助格式化这些值

我的问题是,鉴于我的结构,我将如何做到这一点?

// Data
const data = {
  header: {
    date: {
      feature: "Date",
      format: "date",
    },
    description: {
      feature: "Description",
      format: "text",
    },
    salary: {
      feature: "Salary",
      format: "currency",
    },
    score: {
      feature: "Score",
      format: "float",
    },
    useage: {
      feature: "Useage",
      format: "percentage",
    },
  },
  items: [
    {
      date: "2021-02-08",
      description: "Light",
      salary: "50000",
      score: 20,
      useage: 20,
    },
    {
      date: "2021-02-08",
      description: "Heavy",
      salary: "60000",
      score: 50.235,
      useage: 30,
    }
  ],
};

// format helper
export const getFormat = (value: any, format: FormatProp) => {
  let options: Intl.NumberFormatOptions;

  switch (format) {
      case "currency":
          options = { currency: "USD", style: "currency" };
          break;
      case "percentage":
          options = { style: "percent", maximumFractionDigits: 2 };
          break;
      case "float":
          options = { maximumFractionDigits: 2, minimumFractionDigits: 2 };
          break;
      case "text":
          return value;
      case "date":
          return value;
      default:
          throw Error("Wrong Format!");
  }
  return Intl.NumberFormat("en-US", options).format(value);
};

export type FormatProp =
  | "currency"
  | "percentage"
  | "float"
  | "text"
  | "date";

所需 Output

[{
  date: "2021-02-08",
  description: "Light",
  salary: "$50,000.00",
  score: "20.00",
  useage: "20%",
},
{
  date: "2021-02-08",
  description: "Heavy",
  salary: "$60,000",
  score: "50.24",
  useage: "30%",
}]

您将需要遍历数据项以获得具有正确 output 的新数组。 您还希望通过 object 中的每个key go 并根据键是什么以及它在 header 中的表示方式来格式化相关value

因此,我们将map覆盖项目,使用Object.entries获取key数组和关联value ,然后使用reduce键逐键构建项目 object。

 // Data const data = { header: { date: { feature: 'Date', format: 'date', }, description: { feature: 'Description', format: 'text', }, salary: { feature: 'Salary', format: 'currency', }, score: { feature: 'Score', format: 'float', }, useage: { feature: 'Useage', format: 'percentage', }, }, items: [{ date: '2021-02-08', description: 'Light', salary: '50000', score: 20, useage: 20, }, { date: '2021-02-08', description: 'Heavy', salary: '60000', score: 50.235, useage: 30, }, ], }; // format helper const getFormat = (value, format) => { let options; switch (format) { case 'currency': options = { currency: 'USD', style: 'currency' }; break; case 'percentage': options = { style: 'percent', maximumFractionDigits: 2 }; break; case 'float': options = { maximumFractionDigits: 2, minimumFractionDigits: 2 }; break; case 'text': return value; case 'date': return value; default: throw Error('Wrong Format;'). } return Intl,NumberFormat('en-US'. options);format(value); }. const output = data.items.map(item => // iterate over items Object.entries(item),reduce((a, [key. value]) => { // for each item get the key/value pairs const header = data;header[key]. // find the associated header return {..,a: [key]? header, getFormat(value. header:format), value, // if the header exists lets get the formatted value back; else return the previous value }, }, {}); ). console;log(output);

键入响应

我们知道我们的未格式化版本包含stringnumber值的混合。 我们还知道,当我们完成格式化时,所有的值都将被转换为string

我们知道data.header包含与data.header元素相同的键,并且data.items中的值包含属性format format的值是FormatPropstring ,具体取决于您想要的严格程度。 如果使用string会更容易,因为 typescript 会将data object 中的字符串值解释为string而不是它们的文字值(除非使用as const )。

综上所述,API 响应可以键入为:

type Data<Keys extends string> = {
  header: Record<Keys, {
      feature: string;
      format: string;
    }>,
  items: Array<Record<Keys, number | string>>
}

键入 Map 函数

我们知道 Intl.NumberFormat 的.format() Intl.NumberFormat只接受number ,所以传递一个any类型的值并不是最好的。 我们可以做得更好。 让我们只传递number值。 我们将让与string字段相关的案例抛出错误。

export const getFormat = (value: number, format: FormatProp): string => {
  let options: Intl.NumberFormatOptions;

  switch (format) {
      case "currency":
          options = { currency: "USD", style: "currency" };
          break;
      case "percentage":
          options = { style: "percent", maximumFractionDigits: 2 };
          break;
      case "float":
          options = { maximumFractionDigits: 2, minimumFractionDigits: 2 };
          break;
      default:
          throw Error("Wrong Format!");
  }
  return Intl.NumberFormat("en-US", options).format(value);
};

如果这是您在Data中所做的,那么如果我们允许任何string作为format实际上会更好。 但是为了自动完成,我将FormatPropstring保持在一个联合中。

export const getFormat = (value: number, format: FormatProp | string): string => {

映射时,它与@Mateusz 所做的基本相同,只是我们需要一些额外的注释。 具体来说,我们必须键入空的 object {} ,我们将其reduce为完整 object Record<Keys, string>的类型,并且我们必须从Object.entries中键入数组作为[Keys, string | number][] [Keys, string | number][]以便将键作为其文字值而不仅仅是string

const formatItems = <Keys extends string>(data: Data<Keys>): Array<Record<Keys, string>> => {
  return data.items.map( item => {
    // we have to assert the correct type here with `as` or else all keys are `string`
    return (Object.entries(item) as [Keys, string | number][]).reduce( 
      (formatted, [key, value]) => {
        formatted[key] = typeof value === "number" ? getFormat(value, data.header[key].format) : value;
        return formatted;
    }, {} as Record<Keys, string>); // assert the type for the incomplete object
  })
}

使用此特定data object 调用formatItems(data)现在返回以下类型:

Record<"date" | "description" | "salary" | "score" | "useage", string>[]

您可以访问 object 上的任何这些属性,并且 typescript 知道这些属性始终存在并且始终为string类型。

由于我们使用了通用Keys而不是依赖于这个特定的 object 类型,因此您可以使用其他数据集调用formatItems并获得类似类型良好的响应。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM