Home > Software engineering >  Lookup value from another array and group keys
Lookup value from another array and group keys

Time:03-07

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 :

  1. Since im checking the next item in the array to see if the title matches, then there is an extra unrequired "&"
  2. Is there an easier way to accomplish the same ? (Using shorthand, filters etc?)

CodePudding user response:

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:

  • once to join the titles: ['Warrior', 'Journey'] to au=Warrior|au=Journey
  • once to join the joined titles from all the typenames into a single string

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);

CodePudding user response:

Use arrays to handle tokenization

This is a standard trick for intelligently getting "glue" right when assembling strings.

  1. Start by creating an array of items
  2. Use Array.prototype.join to produce the final string

I'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('&')

CodePudding user response:

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))

  • Related