如何在过滤和映射后获取用户ID

7

我试图为每个服务创建一个包含名称和用户ID的对象,我希望该对象如下所示:

const object = {
    name: Netflix, user: (user id who pays max price for this provider)
    name: Comcast, user: (same)
    name: Verizon, user: (same)
}

我已经尝试更改地图返回中的对象属性,但它不起作用。另外,我已经完成了对象的一半,我的意思是已经有了提供者名称,现在我需要其他键值对。

const services = [
     { userid: 1, providerId: 1, amount: 250000 },
     { userid: 4, providerId: 3, amount: 280900 },
     { userid: 6, providerId: 3, amount: 31000 },
     { userid: 2, providerId: 2, amount: 58600 },
     { userid: 3, providerId: 1, amount: 13000 },
     { userid: 5, providerId: 2, amount: 5000 },
     { userid: 3, providerId: 3, amount: 59900 },
     { userid: 6, providerId: 3, amount: 9500 }
]

const providers = [
    { id: 1, name: Netflix },
    { id: 2, name: Comcast },
    { id: 3, name: Verizon }
]

这是我的函数。
function getUserId(providerId) {
    return services.filter(function(obj) {
        if (obj.providerId == providerId)
            return obj.providerId;
    }).map(function(obj) { return obj.amount });
}

function getMaxUserId(providerId) {
    return Math.max(...getUserId(providerId));
}

providers.forEach(prov => {
    object[prov.name] = getUserId(prov.id);
})

如您所见,首先我会筛选整个数组,寻找特定providerId的提供商,然后使用map函数创建一个新数组,其中包含该特定提供商的所有“amounts”,最后我会在该数组中查找最大金额,这一切都有效,它将每个提供商的正确最大金额返回,但是我还想获取为每个提供商支付最多的用户id。

实际上,我是这样获取对象的:

[
   {name: Netflix, user: 250000},
   {name: Comcast, user: 58600},
   {name: Verizon, user: 280900}
]

我需要这个:

[
   {name: Netflix, user: 1},
   {name: Comcast, user: 2},
   {name: Verizon, user: 4}
]

第一个代码示例中您所期望的输出是不可能的。对象不能有重复的键。编辑问题以解决这个问题将使其更容易理解。 - Mark
我不明白你的意思,@Mark Meyer。我提供的代码基本上是可以工作的,只是当用户ID支付最大金额时,它返回的是最大金额,而不是最大值。 - Noderjs
@Tibrogargan,我编辑了代码,它不应该是账户,而应该是服务数组,抱歉。 - Noderjs
好的,但你的“用户”来自哪里仍然不清楚。对于名为“services”的东西,大多数人会假设“id”是服务的唯一标识符。在您的问题中看不到任何合理地标识用户的内容。 - Tibrogargan
嗨,Node.js!不好意思打擾了——除了參考問題之外,只需引用 obj.id 而不是 obj.ammount——就我所理解的目標而言,您的 getUserId() 函数將 ammount 映射到輸出 Array(我認為 services 元素中的 id 鍵引用用戶 ID,對吧?) - Oleg Valter is with Ukraine
显示剩余3条评论
3个回答

3

您也可以按照数量对服务进行排序。然后,您可以通过providerId将它们进行分组,最后映射提供者以获得所需的输出:

const services = [ { id: 1, providerId: 1, amount: 250000 }, { id: 4, providerId: 3, amount: 280900 }, { id: 6, providerId: 3, amount: 31000 }, { id: 2, providerId: 2, amount: 58600 }, { id: 3, providerId: 1, amount: 13000 }, { id: 5, providerId: 2, amount: 5000 }, { id: 3, providerId: 3, amount: 59900 }, { id: 6, providerId: 3, amount: 9500 } ]
const providers = [ { id: 1, name: 'Netflix' }, { id: 2, name: 'Comcast' }, { id: 3, name: 'Verizon' } ] 

let grouped = services
  .sort((a,b) => b.amount - a.amount)  // sort `desc`
  .reduce((r,c) => ((r[c.providerId] = r[c.providerId] || []).push(c), r), {})
 
let result = providers.map(p => ({name: p.name, user: grouped[p.id][0].id}))

console.log(result)

您还可以跳过上面的sort,并检查amount,然后根据需要执行pushunshift操作:

const services = [ { id: 1, providerId: 1, amount: 250000 }, { id: 4, providerId: 3, amount: 280900 }, { id: 6, providerId: 3, amount: 31000 }, { id: 2, providerId: 2, amount: 58600 }, { id: 3, providerId: 1, amount: 13000 }, { id: 5, providerId: 2, amount: 5000 }, { id: 3, providerId: 3, amount: 59900 }, { id: 6, providerId: 3, amount: 9500 } ]
const providers = [ { id: 1, name: 'Netflix' }, { id: 2, name: 'Comcast' }, { id: 3, name: 'Verizon' } ] 

let groupedById = services.reduce((acc, cur) => { 
  let k = cur.providerId
  acc[k] = acc[k] || []
  if(acc[k][0] && acc[k][0].amount > cur.amount) acc[k].push(cur)
  else acc[k].unshift(cur)
  return acc
}, {})

let result = providers.map(({name, id}) => ({name, user: groupedById[id][0].id}))

console.log(result)

这样你只需要一个Array.reduce和一个Array.map,比起原来使用forEachfiltermap的版本将会更加高效


1
理想情况下,我们希望在JS中有argmax函数。但是目前来看,正常的迭代方式更加美观。请注意,由于JS将字典键转换为字符串,因此很少看到==运算符。
var maxes = {};
services.forEach(item => {
    if(!maxes[item.providerId] || maxes[item.providerId] < item.amount[0]) {
        maxes[item.providerId] = [item.amount, item.id];
    }
});
function providerById(id) {
    return providers.filter(p => p.id == key)[0].name;
}
console.log(Object.keys(maxes).map(key => {
    return { name: providerById(key), user: maxes[key][1] };
}));

1

作为对您的问题的第三个可能解决方案(根据我的评论),您可以使用类似于以下内容的东西(您可以根据自己的喜好修改对象,但我建议完全摆脱 nameuser 键):

const object = {};

function getUserId(providerId) {
    return services.filter(function(obj) {
        if (obj.providerId == providerId)
            return obj.providerId;
    }).map(function(obj) { return obj });
}

function getMaxUserId(output) {
    let amm = output.map( (o) => { return o.amount; } );
    let idx = amm.indexOf(Math.max(...amm));
    return output[idx].userid;
}

providers.forEach(prov => {
    object[prov.name] = getMaxUserId(getUserId(prov.id));
})

console.log(object)

使用此解决方案,您的输出将如下所示:
Object { Netflix: 1, Comcast: 2, Verizon: 4 }

当然,只需进行小调整即可返回原本意图的带有对象的 Array (不要忘记将 object 分配给 []):
providers.forEach(prov => {
  object.push( {name:prov.name,user:getMaxUserId(getUserId(prov.id))} )
})

在后一种情况下,输出将如下所示:
Array [Object { name: "Netflix", user: 1 }, Object { name: "Comcast", user: 2 }, Object { name: "Verizon", user: 4 }]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接