Typescript -按多个属性分组

x6yk4ghg  于 2023-04-22  发布在  TypeScript
关注(0)|答案(5)|浏览(219)

我有一个帐户的名称数组。每个人可以有一个以上的类型。类型是一个枚举..它应该替换其定义的值。希望按accountNumber,firstName,lastName分组,而不是类型。

export interface Name {
    accountNumber: string;    
    firstName: string|null;
    lastName: string|null;
     type: AccountType;
}
export enum AccountType{
    primary = 0,
    secondary = 1
    
}
const names =[
   {
      "accountNumber":"0000001420",
      "firstName":"KELLY",
      "lastName":"WHITE",
      "type":0
   },
   {
      "accountNumber":"0000001420",
      "firstName":"RAY",
      "lastName":"WHITE",
      "type":0
   },
   {
      "accountNumber":"0000001420",
      "firstName":"KELLY",
      "lastName":"WHITE",
      "type":1
   }
]

我想要一个具有以下格式的输出数组:

[{
  firstName:"KELLY",
  lastName:"WHITE",
  accountNumber:"0000001420",
  types: [primary, secondary]
}, {
  firstName: "RAY",
  lastName: "WHITE",
  accountNumber: "0000001420",
  types: [primary]
}]

我试着用Reduce…

const merged2 = names.reduce((r, { accountNumber, firstName, lastName,type }) => {
  const key = `${accountNumber}-${firstName}-${lastName}`;
  
  r[key] = r[key] || { accountNumber, firstName, lastName, types: [] };
  r[key]["types"].push(type)
  return r;
}, {})

我在r[key]处出错

元素隐式具有“any”类型,因为“string”类型的表达式不能用于索引类型“{}”。在类型“{}”上未找到具有“string”类型参数的索引签名。

如何修复错误,或者是否有其他方法来获得结果,而不是Reduce函数?
以下是TS Playground的链接:https://www.typescriptlang.org/play?ssl=20&ssc=2&pln=1&pc=1#code/MYewdgzgLgBGCGBbAphGBeA2gKBng3rnsQEQqIBGyATgHICulNJAXCQAydcCMALAEzsSAGiLEYJAGYBLatFpJkrEgGkAogBkNATRFjSAG3jzFygOoAJAJIAVNXvF4SUAJ4AHJS3ZiAvqOKEjhLkVHSMocpcPAJC-o5SsiYoygBKAIK6ceIkRkmeJJa29lmkrh6s3sR+YoHxITQMTNSRUex8gg7xMnJQCsls6lqZ+k65vaZshXad2WWe3L7YALrY2NJgUDSS8MDIMH3IhNg+q6CQsCjUAObIACYYcIoQAHTUd-S7ABSf1MIw+DB6mEmn9unk-mMDsJnjC3tAYD4AJQYAB8-yIZ3hAGtkC4HgADAAk+CBjVCPgAtMSweMUJTiZDFD58QBuPBEaiYHEuJYPTnc3kAH0F-0ByCaZJooMStOQEOMsr+cwgLBgmF5PhZHK5uKWmGc7lQJCWzzc9AgAAsfqgoIiOcgoPRqGAYNQtX5-kjTuB4Zcbrd+A8ECgXm9bh9kN9fqLSeEpTAaVCYIyUMI5gjkeg0bVMbBuQTibGmvT8ImmVT8CnkMy2TBtQK+TqeTBhTHxaFJdGy6nkwqk8rVeqEVq8PzdfrlcbTearXM7aOHU6XW7jn98F7sLmYFBpCgbPAKAZkNwHgB5CgAK2QwCgzwAbvADPRUJ8-Xd+HbMSAj88DCArp8O57geR7cIiQA

vlju58qv

vlju58qv1#

您可以尝试使用Map,使用帐号作为密钥,假设它们都是唯一的。

export interface Name {
    accountNumber: string;    
    firstName: string|null;
    lastName: string|null;
    type: AccountType;
}

export interface NameEntry {
    accountNumber: string;    
    firstName: string|null;
    lastName: string|null;
    types: AccountType[];
}

export enum AccountType{
    primary = 0,
    secondary = 1
}

// omitted names variable for length

const nameMap = new Map<string, NameEntry>();

names.forEach((name) => {
    if (nameMap.has(name.accountNumber)) {
        nameMap.get(name.accountNumber).types.push(name.type);
    } else {
        nameMap.set(name.accountNumber, {
            accountNumber: name.accountNumber,
            firstName: name.firstName,
            lastName: name.lastName,
            types: [name.type],
        });
    }
})

然后你可以像这样遍历map:

// the value will be of type NameEntry
nameMap.forEach((value) => {
    console.log(value);
})

并获取给定帐号的条目:

let accountNum = "0000001420";

let entry = nameMap.get(accountNum);

console.log(entry); // {
                    //       "accountNumber":"0000001420",
                    //       "firstName":"KELLY",
                    //       "lastName":"WHITE",
                    //       "types":[0,1]
                    // }
x4shl7ld

x4shl7ld2#

你只需要显式地为r声明一个类型,即使声明为any也可以

const merged = names.reduce((r: any, { memberNumber, firstName, lastName,...rest }) => {

也可以为Map声明一个类型

export interface GroupedName {
    accountNumber: string;    
    firstName: string|null;
    lastName: string|null;
    types: AccountType[];
}
interface GroupedNames {
    [key: string]: GroupedName;}

const merged = names.reduce((r: GroupedNames, { memberNumber, firstName, lastName,...rest }) => {

要得到相同的结果,可以使用简单的for循环

const groupedNames: GroupedNames = {};
for(let i =0; i< names.length; i++) {
  const {accountNumber, firstName, lastName, type} = names[i];
  const key = `${accountNumber}-${firstName}-${lastName}`;
  if(!groupedNames[key]) {
     accountNumber, firstName, lastName, types:[]
  };

  groupedNames[key].types.push(type):

}
3pvhb19x

3pvhb19x3#

lodash groupby很容易使用。然后只需在值上绘制一个小Map,就可以开始了。易于阅读和推理,不像reduce需要一些时间来适应。https://lodash.com/docs/4.17.15#groupBy

const res = groupBy(names, (name) => name.accountNumber+name.firstName+name.lastName)

你会得到一个像这样的对象:

const res = {
    "0000001420KELLYWHITE": [
        {
            "accountNumber": "0000001420",
            "firstName": "KELLY",
            "lastName": "WHITE",
            "type": 0
        },
        {
            "accountNumber": "0000001420",
            "firstName": "KELLY",
            "lastName": "WHITE",
            "type": 1
        }
    ],
    "0000001420RAYWHITE": [
        {
            "accountNumber": "0000001420",
            "firstName": "RAY",
            "lastName": "WHITE",
            "type": 0
        }
    ]
}

如果你只需要这些值,你可以使用Object.values(res)。
获取每个值的类型并与其他值相加。

const answer = Object.values(res).map(array => {
    const types = array.map(obj => obj.type)
    const {type, ...returnObj} = array[0]
    return {...returnObj, types}
})

最终结果为:

[
    {
        "accountNumber": "0000001420",
        "firstName": "KELLY",
        "lastName": "WHITE",
        "types": [
            0,
            1
        ]
    },
    {
        "accountNumber": "0000001420",
        "firstName": "RAY",
        "lastName": "WHITE",
        "types": [
            0
        ]
    }
]
egdjgwm8

egdjgwm84#

第一个问题是需要指定reduce累加器的类型(代码中的r)。
第二个问题是最后一行:}, {}),它应该是}, []),这样你就可以用一个空数组初始化你的累加器。
对于转换0 -〉primary和1 -〉secondary,我们可以使用AccountType[AccountType.primary]AccountType[AccountType.secondary]。问题是返回的值将是string类型。有关枚举转换的更多信息,请查看例如以下答案:https://stackoverflow.com/a/73325300/14072498
一个直接的解决方案可以是使用reduce和一个单独的接口来处理结果数组,比如@boreddad420。

export interface AccumulatedName {
    accountNumber: string;
    firstName: string | null;
    lastName: string | null;
    types: string[];
}

const reducedNames = names.reduce(
    (accumulator: AccumulatedName[], currentName) => {
        const findIndex = accumulator.findIndex(it =>
            it.accountNumber === currentName.accountNumber
            && it.firstName === currentName.firstName
            && it.lastName === currentName.lastName
        )

        if (findIndex < 0) {
            accumulator.push({
                accountNumber: currentName.accountNumber,
                firstName: currentName.firstName,
                lastName: currentName.lastName,
                types: [AccountType[currentName.type]]
            })
        } else {
            accumulator[findIndex].types.push(AccountType[currentName.type])
        }
        return accumulator
    }, [])
9njqaruj

9njqaruj5#

我刚刚在你的TS Playground上尝试了这个,似乎工作正常:

const names =[
   {
      "accountNumber":"0000001420",
      "firstName":"KELLY",
      "lastName":"WHITE",
      "type":0
   },
   {
      "accountNumber":"0000001420",
      "firstName":"RAY",
      "lastName":"WHITE",
      "type":0
   },
   {
      "accountNumber":"0000001420",
      "firstName":"KELLY",
      "lastName":"WHITE",
      "type":1
   }
]

enum AccountType {
  primary = 0,
  secondary = 1,
}

interface Name {
  accountNumber: string;
  firstName: string | null;
  lastName: string | null;
  type: AccountType;
}

interface NameEntry {
  accountNumber: string;
  firstName: string | null;
  lastName: string | null;
  types: AccountType[];
}

const nameEntries = Object.values(
  names.reduce((acc: Record<string, NameEntry>, curr: Name) => {
    const key = `${curr.accountNumber}-${curr.firstName}-${curr.lastName}`;
    if (acc[key]) {
      acc[key].types.push(curr.type);
    } else {
      acc[key] = { ...curr, types: [curr.type] };
    }
    return acc;
  }, {})
);

console.log(nameEntries)

相关问题