With this data I am returning one object per keyword
prioritising preferedDomain
string on domain and then its higher rank
value, it is already ordered by rank so returning one object per keyword will take care of that.
This does work and I have done some performance checks and found out that doing a for loop will take longer than this.
Is there a better approach to tackle this problem?
const preferedDomain = 'prefered'
const data = [
{ keyword: 'hey', domain: 'apple', rank: 1},
{ keyword: 'hey', domain: 'apple', rank: 2},
{ keyword: 'hey', domain: 'prefered', rank: 46},
{ keyword: 'foo', domain: 'amazon', rank: 1},
{ keyword: 'foo', domain: 'amazon', rank: 2},
{ keyword: 'foo', domain: 'amazon', rank: 3},
{ keyword: 'bla', domain: 'prefered', rank: 1},
{ keyword: 'bla', domain: 'prefered', rank: 2},
{ keyword: 'bla', domain: 'prefered', rank: 3}
]
// Object map with prefered objects only
const prefered = data.reduce((acc, obj) => {
if (!acc[obj.keyword] && obj.domain === preferedDomain) acc[obj.keyword] = obj
return acc
}, {})
// If keyword is on prefered object use that, otherwise the first entry
const res = Object.values(
data.reduce((acc, obj) => {
if (!acc[obj.keyword]) acc[obj.keyword] = prefered[obj.keyword] ?? obj
return acc
}, {})
)
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }
-
\$\begingroup\$ you can do just one pass, if you overwrite existing object (in the keyword map) when it is not prefered and the currently iterated item is. if the input was not sorted you could also just overwrite existing object when rank is better. \$\endgroup\$slepic– slepic2021年08月27日 08:13:50 +00:00Commented Aug 27, 2021 at 8:13
-
\$\begingroup\$ Thanks for your response, could you demostrate the single pass option? I tried something like that but when it overwrites I always ended up with the last rank. \$\endgroup\$Álvaro– Álvaro2021年08月27日 09:13:02 +00:00Commented Aug 27, 2021 at 9:13
1 Answer 1
To do the job in a single pass you can do it like this:
const preferedDomain = 'prefered'
const data = [
{ keyword: 'hey', domain: 'apple', rank: 1},
{ keyword: 'hey', domain: 'apple', rank: 2},
{ keyword: 'hey', domain: 'prefered', rank: 46},
{ keyword: 'foo', domain: 'amazon', rank: 1},
{ keyword: 'foo', domain: 'amazon', rank: 2},
{ keyword: 'foo', domain: 'amazon', rank: 3},
{ keyword: 'bla', domain: 'prefered', rank: 1},
{ keyword: 'bla', domain: 'prefered', rank: 2},
{ keyword: 'bla', domain: 'prefered', rank: 3}
]
const res = Object.values(
data.reduce((acc, obj) => {
const best = acc[obj.keyword]
if (best) {
const bestDomainIsPrefered = best.domain === preferedDomain
const objDomainIsPrefered = obj.domain === preferedDomain
if (
(objDomainIsPrefered && !bestDomainIsPrefered)
||
(objDomainIsPrefered === bestDomainIsPrefered && obj.rank < best.rank)
) {
acc[obj.keyword] = obj
}
} else {
acc[obj.keyword] = obj
}
return acc
}, {})
)
console.log(res)
Also it might be worth to sacrifice a little performace using a foreach for the sake of readability... Even a Map instead of plain object could help here.
const map = new Map()
for (const obj of data) {
const best = map.get(obj.keyword)
if (!best || ...) {
map.set(obj.keyword, obj)
}
}
return [ ...map.values() ]
On other hand if you are obsessed with performance, you might want to avoid comparing the best.domain
to preferedDomain
more then once by storing the objDomainIsPrefered
into the map along with the object. But I'm not sure at what point, if at all, the gain would overcome the loss for having to create more objects.
const map = new Map()
for (const obj of data) {
const best = map.get(obj.keyword)
if (!best || ...) {
map.set(obj.keyword, { obj, isDomainPrefered: objDomainIsPrefered })
}
}
return [ ...map.values() ].map((envelope) => envelope.obj)
-
\$\begingroup\$ Thanks, what does
if (!best || ...)
means? I get an error with that:SyntaxError: Unexpected token '...'
\$\endgroup\$Álvaro– Álvaro2021年08月27日 14:02:31 +00:00Commented Aug 27, 2021 at 14:02 -
\$\begingroup\$ @Álvaro hehe thats Just a placeholder for the conditions from the first snippet. I didn't want to write them again... \$\endgroup\$slepic– slepic2021年08月27日 19:05:32 +00:00Commented Aug 27, 2021 at 19:05