Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get users id after filter and map

I am trying to create an Object with the name and user id for each service, I want the object to look like this:

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

I have tried changing the object property in the map return but it's not working, also I already have the object half done I mean with the names of the providers now I need the other key,value pair

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

This is my function

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

as you can see first I filter the entire array looking for the providers with certain providerId then I create a new array with map function filled with all the 'amounts' for that particular provider and last I search for the max amount of that array, this all works fine it return the right max amount for each provider BUT I also want to get the user id who's paying the most for each provider

actually I'm getting my object like this:

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

and I need this:

[
   {name: Netflix, user: 1},
   {name: Comcast, user: 2},
   {name: Verizon, user: 4}
]
like image 252
Noderjs Avatar asked Jun 22 '19 01:06

Noderjs


Video Answer


3 Answers

You could also sort your services on the amount. Then you can group them by the providerId and finally map over the providers to get the output you want:

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)

You could also skip the sort above and check the amount and based on that either push or unshift:

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)

This way you only have one Array.reduce and one Array.map which will be more performant than your original version which has forEach, filter and map.

like image 71
Akrion Avatar answered Oct 14 '22 19:10

Akrion


And, as a third possible solution to your problem (following up on my comment), you can use something like this (you can modify the object however you deem more to your liking, but I would suggest getting rid of name and user keys altogether):

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)

The output in your case using this solution will look like this:

Object { Netflix: 1, Comcast: 2, Verizon: 4 }

Ofc, with a small tweak you will be able to return an Array with objects as originally intended (don't forget to assign object to [] instead):

providers.forEach(prov => {
  object.push( {name:prov.name,user:getMaxUserId(getUserId(prov.id))} )
})

In the latter case the output will look like this:

Array [Object { name: "Netflix", user: 1 }, Object { name: "Comcast", user: 2 }, Object { name: "Verizon", user: 4 }]
like image 23
Oleg Valter is with Ukraine Avatar answered Oct 14 '22 18:10

Oleg Valter is with Ukraine


Ideally we'd have argmax in JS. But for now, normal iteration looks nicer. Note the rarely-seen == because JS turns the dictionary keys into strings:

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] };
}));
like image 34
APerson Avatar answered Oct 14 '22 19:10

APerson