Non
Non

Reputation: 8589

How can I group objects alphabetacally within an array by a specific key?

I have an array of objects of countries with some values for every country. But I need to put them in a list, alphabetically.

// globalBrands
{
    [
        {
            id: 1,
            title: 'Argentina',
            content: [{url: 'www.argentinca.com', title: "Argentina Title"}]z
        },
        {
            id: 2,
            title: 'Canada',
            content: [{url: 'www.canada.com', title: "Canada Title"}]
        },
        {
            id: 3,
            title: 'Chile',
            content: [{url: 'www.chile.com', title: "Chile Title"}]
        },
        {
            id: 4,
            title: 'China',
            content: [{url: 'www.china.com', title: "China Title"}]
        },
        {
            id: 5,
            title: 'Finland',
            content: [{url: 'www.finland.com', title: "Finland Title"}]
        },
        {
            id: 5,
            title: 'France',
            content: [{url: 'www.france.com', title: "France Title"}]
        },
    ]
}

I created a new array where the output was supposed to be something like this or something similiar which gives me access to the properties of the countries sorted alphabetically

[
    {
        id: 1,
        letter: 'A',
        country: ['Argentina'],
        content: [{url: 'www.some.com', title: "A Title"}]
    },
    {
        id: 2,
        letter: 'C',
        country: ['Canada', 'Chile', 'China'],
        content: [{url: 'www.canada.com', title: "Canada Title"}, {url: 'www.chile.com', title: "Chile Title"}, {url: 'www.china.com', title: "China Title"}]
    },
    {
        id: 3,
        letter: 'F',
        country: ['Finland', 'France'],
        content: [{url: 'www.finland.com', title: "Finlanda Title"}, {url: 'www.france.com', title: "France Title"}]
    },
]

I have this 2 functions:

function mergeObjectsInUnique<T> (array: T[], property: unknown): T[] {
    const newArray = new Map();

    array.forEach((item: T) => {
      // @ts-ignore
      const propertyValue = item[property];

      if (newArray.has(propertyValue)) {
        return newArray.set(propertyValue, { ...item, ...newArray.get(propertyValue) });
      }

      return newArray.set(propertyValue, item);
    });

    return Array.from(newArray.values());
}

const sortBrands = (): NewBrandInterface[] => {
    let newArr: NewBrandInterface[] = [];

    for (let i = 0; i < globalBrands.length; i++) {
      const itemIdx = globalBrands[i];

      // This structure can be change to whatever is better
      newArr = [
        ...newArr,
        ...[{
          id: itemIdx.drupal_id,
          letter: itemIdx.title.charAt(0),
          country: [itemIdx.title],
          content: itemIdx.content.map(g => ({
            url: g.url,
            title: g.title
          }))
        }]
      ];
    }

    return mergeObjectsInUnique(newArr, 'letter');
};

That code what it does is to just return one item in the country array. Like for the letter C there are 3 titles/countries that start with the letter C, Canada, Chile and China, so all of them should be returned within the country array and its proper values (url, title). The same for example for Finland and France.

What am I doing wrong?

If you see the output on the playground, it kinda deletes the items and just leaves one with the starting letter. For the C it only leaves there Canada but it removes the other 2.

I created an example with the exact same code I posted, here.

Upvotes: 3

Views: 162

Answers (1)

pilchard
pilchard

Reputation: 12919

Your question is a little confusing, but if you are looking to refactor your input array grouped by letter you can use a single reduce() call.

const input = [{ id: 1, title: 'Argentina', content: [{ url: 'www.argentinca.com', title: "Argentina Title" }] }, { id: 2, title: 'Canada', content: [{ url: 'www.canada.com', title: "Canada Title" }] }, { id: 3, title: 'Chile', content: [{ url: 'www.chile.com', title: "Chile Title" }] }, { id: 4, title: 'China', content: [{ url: 'www.china.com', title: "China Title" }] }, { id: 5, title: 'Finland', content: [{ url: 'www.finland.com', title: "Finland Title" }] }, { id: 5, title: 'France', content: [{ url: 'www.france.com', title: "France Title" }] },];

let i = 1;
const result = Object.values(
  input.reduce((acc, { id, title, content }) => {
    const letter = title[0]

    acc[letter] = acc[letter] || { id: i++, letter, country: [], content: [] }
    // or using logical nullish assignment
    // acc[letter] ??= { id: i++, letter, country: [], content: [] }

    acc[letter].country.push(title);
    acc[letter].content.push(...content);

    return acc
  }, {})) // initialize accumulator as an empty object

console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 3

Related Questions