-1

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

  • you need quotes around strings. – Barmar Jun 14 '22 at 00:01
  • You need a recursive procedure to total the clicks. But you need to prevent loops, so the recursion needs to remember all the nodes that it has traversed and check the current node against this to prevent hitting it again. – Barmar Jun 14 '22 at 00:02
  • You don't need recursion if all variants are contained in every object @Barmar. – Jack Bashford Jun 14 '22 at 00:07
  • you can do reduce to find max object and then do map – Nonik Jun 14 '22 at 00:13
  • @KaitlynForks I added an answer. Did you get a chance to look into that ? I hope that will help you in understanding the use case and will work as per your requirement – Debug Diva Jun 16 '22 at 05:53

2 Answers2

0

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);
Debug Diva
  • 26,058
  • 13
  • 70
  • 123
0

Welcome to StackOverflow! Please take the tour, visit the help center 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 minimal reproducible example 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.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103