Reputation: 8589
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
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