Reputation: 10998
I have got 2 arrays tags and typenames:
const tags = [
{
title: 'Warrior',
data: {
type: 'audience'
}
},
{
title: 'Juggler',
data: {
type: 'section'
}
},
{
title: 'Journey',
data: {
type: 'audience'
}
},
{
title: 'Travel',
data: {
type: 'nonexistingtag'
}
}
];
const typenames = [
{ audience: 'au' },
{ section: 'se' },
{ sponsor: 'sp' },
{ contextual: 'co' }
]
The expected output is to create a string that is grouped by keys matching between array1 and array2 type and each key is separated by the pipe "|" symbol and each group is separated by "&" from one another, so for the above sample the final expected output string would be :
au=Warrior|au=Journey&se=Juggler
Here is what I tried so far,
let arr = [];
tags.forEach(element => {
typenames.forEach(el => {
if (Object.keys(el)[0] === element.data.type) // finding matching keys and pushing to an arr
arr.push({ title: Object.values(el)[0], desc: element.title });
})
});
Sort the keys so that they are easy to group :
arr.sort((a, b) => {
let keya = a.title;
let keyb = b.title;
if (keya > keyb) {
return 1;
} else if (keya < keyb) {
return -1;
} else {
// the characters are equal.
return 0;
}
});
Concatenate the string:
let str = '';
arr.forEach(({ title, desc }, i, arr2) => {
if (title === arr2[i + 1]?.title) { // Check that the next item in array has the same title
str = str.concat(`${title}=${desc}|`)
return;
}
str = str.concat(`&${title}=${desc}`)
});
console.log('str', str);
Output :
au=Warrior|&au=Journey&se=Juggler
Issues :
Upvotes: 0
Views: 320
Reputation: 22355
I wil do that this way...
const tags =
[ { title: 'Warrior', data: { type: 'audience' } }
, { title: 'Juggler', data: { type: 'section' } }
, { title: 'Journey', data: { type: 'audience' } }
, { title: 'Travel', data: { type: 'nonexistingtag' } }
]
const typenames =
[ { audience : 'au' }
, { section : 'se' }
, { sponsor : 'sp' }
, { contextual : 'co' }
]
// build jso_K = { audience:{ref:'au',titles:[]},section:{ref:'se',titles:[]},sponsor: ...}
let jso_K = typenames.reduce((r,e)=>
{
let [k,v] = Object.entries(e)[0]
r[k] = {ref:v, titles:[] }
return r
},{})
// complete jso_K on titles --> { audience:{ref:'au',titles:['Warrior','Journey' ] }, section:...
tags.forEach(({title,data})=>
{
if (jso_K.hasOwnProperty(data.type)) jso_K[data.type].titles.push(title)
})
// Build result
let txt = Object.keys(jso_K).reduce((t,k) =>
{
if (jso_K[k].titles.length > 0) t.push( jso_K[k].titles.map(t=>`${jso_K[k].ref}=${t}`).join('|'))
return t
},[]).join('&')
console.log( txt )
Upvotes: 0
Reputation: 2007
You can try this simple and readable method:
const availableTags = [
{
title: 'Warrior',
data: {
type: 'audience'
}
},
{
title: 'Juggler',
data: {
type: 'section'
}
},
{
title: 'Journey',
data: {
type: 'audience'
}
},
{
title: 'Travel',
data: {
type: 'nonexistingtag'
}
}
];
const availableTypenames = [
{
audience: 'au'
},
{
section: 'se'
},
{
sponsor: 'sp'
},
{
contextual: 'co'
}
];
const getResultString = (tags, typenames) => {
let result = '';
for (const typename of typenames) {
const type = Object.keys(typename)[0];
let needSeparate = false;
for (const tag of tags) {
if (type === tag.data.type) {
result +=`${needSeparate ? '|' : '&'}${typename[type]}=${tag.title}`;
needSeparate = true;
}
}
}
return result.slice(1);
}
console.log(getResultString(availableTags, availableTypenames))
Upvotes: 0
Reputation: 371049
Considering the problem, you ultimately want to iterate through all of the typenames and see what values match in the tags - so you should start out by restructuring the tags array to something easier to search through, rather than try to go through the whole somewhat convoluted structure every time. Perhaps an object indexed by the type, eg:
{
audience: ['Warrior', 'Journey'],
section: ['Juggler'],
nonexistingtag: ['nonexistingtag']
}
Then when going through the typenames, all you need to do is look up the linked property on the object, and join twice:
['Warrior', 'Journey']
to au=Warrior|au=Journey
It's much easier to use grouped data structures like arrays that you can join instead of adding a separator based on whether the next item in the array has a certain property.
const tags=[{title:"Warrior",data:{type:"audience"}},{title:"Juggler",data:{type:"section"}},{title:"Journey",data:{type:"audience"}},{title:"Travel",data:{type:"nonexistingtag"}}],typenames=[{audience:"au"},{section:"se"},{sponsor:"sp"},{contextual:"co"}];
const titlesByType = {};
for (const { title, data: { type } } of tags) {
titlesByType[type] ??= [];
titlesByType[type].push(title);
}
const combinedStrs = [];
for (const typeObj of typenames) {
const [[key, abbrev]] = Object.entries(typeObj);
if (titlesByType[key]) {
combinedStrs.push(titlesByType[key].map(str => `${abbrev}=${str}`).join('|'));
}
}
const output = combinedStrs.join('&');
console.log(output);
Upvotes: 1
Reputation: 9137
This is a standard trick for intelligently getting "glue" right when assembling strings.
Array.prototype.join
to produce the final stringI'm thinking something like this:
let items = arr.reduce((output, { title, desc }, i, fullArray) => {
if (title === fullArray[i + 1]?.title) {
output.push(`${title}=${desc}|`)
}
return output
}, [])
let str = items.join('&')
Upvotes: 0