I have an array of objects called articles
. An article has 3 important properties. name
, clicks
, and variants
. The variants
property is also an array of objects that contain name
(It corresponds to an article in the array articles
). What is the most efficient way to find each article's most clicked variant?
I thought maybe there was some way to use a map or spread operator, but I don't understand the syntax. I think I could create a 'dictionary' with the most clicked article for each set, but is there an easier way?
Input:
[
{name: "article1",
clicks: 10,
variants: ["article2", "article3"]},
{name: "article2",
clicks: 7,
variants: ["article1", "article3"]},
{name: "article3",
clicks: 15,
variants: ["article1", "article2"]},
{name: "article4",
clicks: 3,
variants: ["article5"]},
{name: "article5",
clicks: 6,
variants: ["article4"]},
]
Desired output:
{name: "article1",
clicks: 10,
variants: ["article2", "article3"],
mostClickedVariant: "article3"}
{name: "article2",
clicks: 7,
variants: ["article1", "article3"],
mostClickedVariant: "article3"},
etc. for each article
CodePudding user response:
Try this (I added all the descriptive comments in the below code snippet) :
// Input array
const arr = [
{name: "article1",
clicks: 10,
variants: ["article2", "article3"]},
{name: "article2",
clicks: 7,
variants: ["article1", "article3"]},
{name: "article3",
clicks: 15,
variants: ["article1", "article2"]},
{name: "article4",
clicks: 3,
variants: ["article5"]},
{name: "article5",
clicks: 6,
variants: ["article4"]},
];
// Iterating over an input array.
const res = arr.map(obj => {
// Fetching variant objects in an array based on the variants name we have in the array.
const variantArr = obj.variants.map(variant => arr.find(o => o.name === variant));
// Getting most clicked variant object by sorting the variantArr in desc order based on the clicks.
const mostClickedVariant = variantArr.sort((a, b) => b.clicks - a.clicks)[0];
// creating a new property 'mostClickedVariant' in each obejct and assigning the name of the most clicked variant.
obj.mostClickedVariant = mostClickedVariant.name;
return obj;
});
// Expected output
console.log(res);
CodePudding user response:
Welcome to [so]! Please take the [tour], visit the [help] and read up on asking good questions. After doing some research and searching for related topics on SO, try it yourself. If you're stuck, post a [mcve] of your attempt and note exactly where you're stuck. People will be glad to help.
Because someone has already posted a working answer, I won't hold back my approach, which is quite different from that one. Usually I would until the asker shows more effort.
This version does not mutate the original, just returns a copy with the new properties added. It also uses a more efficient way to find maxima, reducing the list to its maximum in O (n)
time, rather than using an O (n log n)
sort
.
const maxBy = (fn) => (xs) => xs .reduce (
({m, mx}, x, _, __, v = fn (x)) => v > m ? {m: v, mx: x} : {m, mx},
{m: -Infinity}
) .mx
const addMostClicked = (
articles,
index = new Map (articles .map (({name, clicks}) => [name, clicks])),
findMax = maxBy((x) => index .get (x))
) => articles .map (
(article) => ({... article, mostClickedVariant: findMax (article .variants)})
)
const articles = [{name: "article1", clicks: 10, variants: ["article2", "article3"]}, {name: "article2", clicks: 7, variants: ["article1", "article3"]}, {name: "article3", clicks: 15, variants: ["article1", "article2"]}, {name: "article4", clicks: 3, variants: ["article5"]}, {name: "article5", clicks: 6, variants: ["article4"]}]
console .log (addMostClicked (articles))
.as-console-wrapper {max-height: 100% !important; top: 0}
We first create an index mapping the article names to their click counts, and, using the helper maxBy
, we create a function, findMax
that will find the name associated with the largest click count.
Then we simply map over the elements, returning copies with new mostClickedVariant
properties determined by calling findMax
with the list of variant names.