使用 reduce 操作对象数组,缺少正确的类型

Posted

技术标签:

【中文标题】使用 reduce 操作对象数组,缺少正确的类型【英文标题】:Manipulating array of objects using reduce, correct types are missing 【发布时间】:2021-03-02 09:34:24 【问题描述】:

使用reduce操作对象数组,缺少正确的类型

我从 API 响应中获取了我需要操作的数组,然后才能在我的节点应用程序中使用它。

这是我已经在我的应用中所做的:

const array = [
  
    users: 
      'bc1bff64-ecde-4500-b16d-c1d3a37f2558': 
        points: 368,
        user_id: 'bc1bff64-ecde-4500-b16d-c1d3a37f2558',
        period: 6,
        city: 'london'
      ,
      '5124be0c-3faf-444d-9e83-b570ca42c772': 
        points: 358,
        user_id: '5124be0c-3faf-444d-9e83-b570ca42c772',
        period: 6,
        city: 'london'
      
    
  ,
  
    users: 
      'bc1bff64-ecde-4500-b16d-c1d3a37f2558': 
        points: 368,
        user_id: 'bc1bff64-ecde-4500-b16d-c1d3a37f2558',
        period: 6,
        city: 'paris'
      ,
      '5124be0c-3faf-444d-9e83-b570ca42c772': 
        points: 358,
        user_id: '5124be0c-3faf-444d-9e83-b570ca42c772',
        period: 6,
        city: 'paris'
      
    
  ,
  
    users: 
      'bc1bff64-ecde-4500-b16d-c1d3a37f2558': 
        points: 0,
        user_id: 'bc1bff64-ecde-4500-b16d-c1d3a37f2558',
        period: 5,
        city: 'london'
      ,
      '5124be0c-3faf-444d-9e83-b570ca42c772': 
        points: 0,
        user_id: '5124be0c-3faf-444d-9e83-b570ca42c772',
        period: 5,
        city: 'london'
      
    
  ,
  
    users: 
      'bc1bff64-ecde-4500-b16d-c1d3a37f2558': 
        points: 0,
        user_id: 'bc1bff64-ecde-4500-b16d-c1d3a37f2558',
        period: 5,
        city: 'paris'
      ,
      '5124be0c-3faf-444d-9e83-b570ca42c772': 
        points: 0,
        user_id: '5124be0c-3faf-444d-9e83-b570ca42c772',
        period: 5,
        city: 'paris'
      
    
  
];

const getPeriodName = (period: number) =>
  ( 5: 'PERIOD_5_NAME', 6: 'PERIOD_6_NAME' [period]);
const getCityName = (city: string) =>
  ( 'london': 'London', 'paris': 'Paris' [city]);
const getPointsGain = (points: number) => points + 'blabla';

const reduced = array.reduce((acc,  users ) => 
  Object.entries(users)
    .map(([id,  ...all ]) => 
      acc[id] = acc[id] || 
        id, periods: 
      ;
      acc[id].periods[all.period] = acc[id].periods[all.period] || 
        name: getPeriodName(all.period),
        cities: 
      ;
      acc[id].periods[all.period].cities[all.city] = 
        name: getCityName(all.city),
        points: all.points,
        pointsGain: getPointsGain(all.points)
      ;
    );
  return acc;
, );
const result = Object.values(reduced);
console.log('result', result);

问题是 reduced 是类型 空对象而不是正确的完整类型

“正确”类型的示例

我想以正确的类型结束这个数组:

[
  
    "id": "bc1bff64-ecde-4500-b16d-c1d3a37f2558",
    "periods": 
      "5": 
        "name": "PERIOD_5_NAME",
        "cities": 
          "london": 
            "name": "London",
            "points": 0,
            "pointsGain": "0blabla"
          ,
          "paris": 
            "name": "Paris",
            "points": 0,
            "pointsGain": "0blabla"
          
        
      ,
      "6": 
        "name": "PERIOD_6_NAME",
        "cities": 
          "london": 
            "name": "London",
            "points": 368,
            "pointsGain": "368blabla"
          ,
          "paris": 
            "name": "Paris",
            "points": 368,
            "pointsGain": "368blabla"
          
        
      
    
  ,
  
    "id": "5124be0c-3faf-444d-9e83-b570ca42c772",
    "periods": 
      "5": 
        "name": "PERIOD_5_NAME",
        "cities": 
          "london": 
            "name": "London",
            "points": 0,
            "pointsGain": "0blabla"
          ,
          "paris": 
            "name": "Paris",
            "points": 0,
            "pointsGain": "0blabla"
          
        
      ,
      "6": 
        "name": "PERIOD_6_NAME",
        "cities": 
          "london": 
            "name": "London",
            "points": 358,
            "pointsGain": "358blabla"
          ,
          "paris": 
            "name": "Paris",
            "points": 358,
            "pointsGain": "358blabla"
          
        
      
    
  
]

【问题讨论】:

【参考方案1】:

当使用reduce 合并到单个对象中时,即使在纯 javascript 中,代码也可能有些冗长。可以说是not semantically appropriate either。使用 TypeScript,情况更糟,因为您必须键入在每次迭代中传递的累加器。

考虑在外部创建对象,并列出其类型,然后使用for..of 遍历数组。以下编译没有 TS 错误:

type Periods = 
  [period: number]: 
    name: string;
    cities: 
      [city: string]: 
        name: string;
        points: number;
        pointsGain: string;
      
    
  
;
const acc: 
  [id: string]:  id: string, periods: Periods 
 = ;
for (const  users  of array) 
  Object.entries(users)
    .forEach(([id,  ...all ]) => 
      acc[id] = acc[id] || 
        id, periods: 
      ;
      acc[id].periods[all.period] = acc[id].periods[all.period] || 
        name: getPeriodName(all.period),
        cities: 
      ;
      acc[id].periods[all.period].cities[all.city] = 
        name: getCityName(all.city),
        points: all.points,
        pointsGain: getPointsGain(all.points)
      ;
    );

const result = Object.values(acc);

请注意,由于您在Object.entries 之后执行副作用,因此您应该使用forEach,而不是.map。 (仅当您需要通过转换初始数组的所有元素来创建新数组时才使用.map

【讨论】:

我想过写界面,但问题是当我在那里使用真实数据时不仅仅是一个 points 键,所以如果我需要更新一些东西,我必须手动更改界面或类型理想的。有没有办法使用自动生成的类型? 我使用的是type,而不是接口。不,当您需要在 TypeScript 中重新组织数据时,除了详尽地指定新数据结构的类型之外,通常没有其他选择。 为什么?例如,如果您这样做 hastebin.net/zurofacuxy.js 也正在重新组织数据,那么您为什么要将 number[] 分配给 array2(如果它已经存在)。 对于像这样的非常微不足道的操作,是的,TypeScript 可以自动推断它 - 但对于几乎任何更复杂的东西,如果不是单个转换,则无法自动推断使用内置的 JavaScript 函数,或者如果执行的过程是非函数式的(TS 最适合函数式范例,但大量代码使用非函数式范例最有意义) 我想你可以获得 partway 的一种方法是运行应用转换的 JavaScript 代码,然后将生成的对象复制到 TypeScript IDE,然后复制 IDE 的该代码的自动检测类型。但是具有动态属性名称的对象必须手动修复(例如这里的ids)。

以上是关于使用 reduce 操作对象数组,缺少正确的类型的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 基于reduce方法实现数组内对象的同属性运算

Redux 能够将字符设置为数组,但是删除操作似乎没有到达 reducer

在 Redux reducer 中对对象数组进行排序

JS中for...in 语句用于对数组或者对象的属性进行循环操作吗?

如何在reducer中的数组索引处添加对象

Swift中 Map,Flatmap,Filter,Reduce的用法