1

consider this array of object:

const relatedSites = [
  {
    "SubdomainName": "client1",
    "ClientName": "Eastern Region",
    "ClientAlias": "eastern-region"
  },
  {
    "SubdomainName": "client1",
    "ClientName": "City of Knox",
    "ClientAlias": "knox"
  },
  {
    "SubdomainName": "client2",
    "ClientName": "Eastern Region",
    "ClientAlias": "eastern-region"
  },
  {
    "SubdomainName": "client2",
    "ClientName": "City of Knox",
    "ClientAlias": "knox"
  }
]

I want to group these by the "SubdomainName" property and return an array of newly made objects using reduce so I get that result:

[
  {
    "title": "client1",
    "links": [
      {
        "url": "https://client1.com/eastern-region",
        "displayText": "Eastern Region"
      },
      {
        "url": "https://client1.com/knox",
        "displayText": "City of Knox"
      }
    ]
  },
  {
    "title": "client2",
    "links": [
      {
        "url": "https://client2.com/eastern-region",
        "displayText": "Eastern Region"
      },
      {
        "url": "https://client2.com/knox",
        "displayText": "City of Knox"
      }
    ]
  }
]

So far this is my solution but I think it looks a bit clunky, is there a more efficient way of doing it? I'm not liking the way I create a new group if I don't find it by title, also pushing the group to the array based on whether its index is found doesn't feel neat. Is there a more elegant way of achieving the same result?

  const groupRelatedSites = (groups, item) => {
    const group = groups.find(gp => gp.title === item.SubdomainName) || { title: item.SubdomainName, links: [] };
    const index = groups.findIndex(group => group.title === item.SubdomainName);
    const link = { url: `https://${item.SubdomainName}.com/${item.ClientAlias}`, displayText: item.ClientName };
    group.links = [...group.links, link];

    if (index === -1) {
      groups.push(group);
    } else {
      groups[index] = group;
    }
    return groups;
  };

  const grouped = relatedSites.reduce(groupRelatedSites, []);
5
  • You're definitely not the first one with that question: How much research effort is expected of Stack Overflow users? Commented Jun 17, 2021 at 8:35
  • @Andreas most example I found on stack return an object not an array and without that manipulation inside the reduce function. Commented Jun 17, 2021 at 8:37
  • You might not find a perfect match, but adjusting one of the many answers is not that complicated (e.g. Object.entries()). Most efficient method to groupby on an array of objects has answers that already return an array. Or this one: How to group an array of objects by key, ... Commented Jun 17, 2021 at 8:42
  • yep exactly my point, makes me wonder if you only read the title of my question and didn't bother with the rest, but thanks. Commented Jun 17, 2021 at 8:46
  • SO is not a free code writing service. There are hundreds of answers that group an array of objects. Many of them already return an array. Your "adjustments" with the title is not a problem of the grouping part. Commented Jun 17, 2021 at 8:47

3 Answers 3

3

You should be able to do this in a few lines using reduce, we create a map using the Subdomain name as the key, then we'll use Object.values to turn the resulting object into an array.

For example:

const relatedSites = [ { "SubdomainName": "client1", "ClientName": "Eastern Region", "ClientAlias": "eastern-region" }, { "SubdomainName": "client1", "ClientName": "City of Knox", "ClientAlias": "knox" }, { "SubdomainName": "client2", "ClientName": "Eastern Region", "ClientAlias": "eastern-region" }, { "SubdomainName": "client2", "ClientName": "City of Knox", "ClientAlias": "knox" } ]; 

const result = Object.values(relatedSites.reduce((acc, el) => { 
    acc[el.SubdomainName] = acc[el.SubdomainName] || { title: el.SubdomainName, links: [] };
    acc[el.SubdomainName].links.push({ url: `https://${el.SubdomainName}.com/${el.ClientAlias}`, displayText: el.ClientName });
    return acc;
}, {}))

console.log(result)

Sign up to request clarification or add additional context in comments.

2 Comments

On the first pass for each SubdomainName, the value acc[el.SubdomainName] will be undefined, so we'll assign a new object to it. On further passes, we'll simply be adding more links. I hope this explains things a little better!
No problem, good luck with your project!!
1

You were close but were making it a little more complicated than need be

const relatedSites = [{
    "SubdomainName": "client1",
    "ClientName": "Eastern Region",
    "ClientAlias": "eastern-region"
  },
  {
    "SubdomainName": "client1",
    "ClientName": "City of Knox",
    "ClientAlias": "knox"
  },
  {
    "SubdomainName": "client2",
    "ClientName": "Eastern Region",
    "ClientAlias": "eastern-region"
  },
  {
    "SubdomainName": "client2",
    "ClientName": "City of Knox",
    "ClientAlias": "knox"
  }
]

let data = relatedSites.reduce((b, a) => {
    let ind = b.findIndex(e => e.title === a.SubdomainName)
    let obj = {
      url: "https://" + a.SubdomainName + ".com/" + a.ClientAlias,
      displayText: a.ClientAlias
    };
    if (ind > -1) {
      b[ind].links.push(obj)
    } else {
      b.push({
        "title": a.SubdomainName,
        "links": [obj]
      })
    }
    return b;
  }, []);

console.log(data)

Comments

0

const relatedSites = [{"SubdomainName": "client1","ClientName": "Eastern Region","ClientAlias": "eastern-region"},{"SubdomainName": "client1","ClientName": "City of Knox","ClientAlias": "knox"},{"SubdomainName": "client2","ClientName": "Eastern Region","ClientAlias": "eastern-region"},{"SubdomainName": "client2","ClientName": "City of Knox","ClientAlias": "knox"}];

const out = relatedSites.reduce((a, b) => {
  const title = b.SubdomainName;
  let obj = a.find(x => x.title === title);
  if (!obj) a.push({
    title: title,
    links: [{ 
      url: `https://${title}.com/${b.ClientAlias}`,
      displayText: b.ClientName,
    }] });
  else obj.links.push({
    url: `https://${title}.com/${b.ClientAlias}`,
    displayText: b.ClientName,
  });
  return a;
}, []);

console.log(out);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.