Knetauge
Knetauge

Reputation: 1

JS: Compare values in Array of Objects and detect matching values

I have an array of objects in Javascript like:

    var arrobj = [
  {'id': 1, 'editors': 'Andrew||Maria', 'authors': 'Dorian||Gabi', 'agents': 'Bob||Peter'},
  {'id': 2, 'editors': 'Dorian||Guybrush', 'author': 'Peter||Frodo', 'agents': 'Dorian||Otto'},
  {'id': 3, 'editors': 'Klaus||Otmar', 'authors': 'Jordan||Morgan', 'agents': 'Jordan||Peter'},
    ];

I need to make a list of all people (editors, authors & agents) occurring in each object with his/her role. The output should contain a new key/value-pair ('involved') looking like this:

'involved': 'Andrew (editor)|| Maria (editor)|| Dorian (author) || Gabi (author) || Bob (agent) || Peter (agent)'

The array of objects should be something like:

   var arrobj = [
  {'id': 1, 'editors': 'Andrew||Maria', 'authors': 'Dorian||Gabi', 'agents': 'Bob||Peter', 'involved': 'Andrew (editor)|| Maria (editor)|| Dorian (author) || Gabi (author) || Bob (agent) || Peter (agent)'},
  {'id': 2, 'editors': 'Dorian||Guybrush', 'authors': 'Peter||Frodo', 'agents': 'Dorian||Otto','involved': 'Dorian (editor, agent) || Gybrush (editor) || Peter (author) || Frodo (author) || Otto (author)'},
  {'id': 3, 'editors': 'Klaus||Otmar', 'authors': 'Jordan||Morgan', 'agents': 'Jordan||Peter','involved': 'Klaus (editor) || Otmar (editor) || Jordan (author, agent) || Morgan (author) || Peter (agent)'},
    ];

If a person is associated to multiple roles (e.g. id 2 --> Dorian occurs in editors & agents), their occurrence in 'involved' should be only once but with both roles in brackets (e.g. Dorian (editor, agent) )

I am very new to programming and cannot think of a way to do it properly. At a first step I guess I have to split all values by "||" into arrays and then compare each name with every other name in the array.

I would really appreciate some help on my problem.

Upvotes: 0

Views: 86

Answers (4)

Ketan Ramteke
Ketan Ramteke

Reputation: 10655

const arrobj = [
  {'id': 1, 'editors': 'Andrew||Maria', 'authors': 'Dorian||Gabi', 'agents': 'Bob||Peter'},
  {'id': 2, 'editors': 'Dorian||Guybrush', 'authors': 'Peter||Frodo', 'agents': 'Dorian||Otto'},
  {'id': 3, 'editors': 'Klaus||Otmar', 'authors': 'Jordan||Morgan', 'agents': 'Jordan||Peter'},
];

for (let obj of arrobj) {
  obj.involved = "";
  for (let key of Object.keys(obj)) {
    let strKey = [...key];
    strKey.pop();
    let role = strKey.join("");
    if (key === "involved") break;
    if (key !== "id") {
      obj.involved += obj[key]
        .split("||")
        .map((name) => name + "(" + role + ")||")
        .join("");
    }
    obj.involved = obj.involved.substr(0, obj.involved.length - 2).trim();
  }
}

console.log(arrobj);

here is the running repl: https://repl.it/join/juljqzxt-theketan2

Upvotes: 0

adiga
adiga

Reputation: 35222

  • map the array and destructure each object to get a roles object without id.

  • Create a map object which will map each person to an array of their roles

  • Loop through each key in the roles object and split at || to get an array of names

  • Loop through the names and update the map object. If the name hasn't been already added, add it using ||= assignment

  • Remove the last character of the role using slice to convert it from plural to singular ("agents" to "agent")

  • The map object now has each person as key and an array of roles as value.

    {
      Dorian: ["editor", "agent"],
      Guybrush: ["editor"],
      Peter: ["author"],
      Frodo: ["author"],
      Otto: ["agent"]
    }
    
  • Loop through the entries of the object and create the involved string

  • return a new object with an additional involved key

const arrobj = [
  {'id': 1, 'editors': 'Andrew||Maria', 'authors': 'Dorian||Gabi', 'agents': 'Bob||Peter'},
  {'id': 2, 'editors': 'Dorian||Guybrush', 'authors': 'Peter||Frodo', 'agents': 'Dorian||Otto'},
  {'id': 3, 'editors': 'Klaus||Otmar', 'authors': 'Jordan||Morgan', 'agents': 'Jordan||Peter'},
];
    
const output = arrobj.map(({ id, ...roles }) => {
  const map = {}
  
  for (const r in roles) {
    const names = roles[r].split("||")
    for (const name of names) {
      map[name] ||= []
      map[name].push(r.slice(0,-1))
    }
  }
  
  const involved = Object.entries(map)
                         .map(([name, values]) => `${name} (${values.join(", ")})`)
                         .join(" || ")
                         
   return { id, ...roles, involved }
})

console.log(output)

Upvotes: 1

Greedo
Greedo

Reputation: 3549

This is an example of solution (ES5), there are some optimization possible. The basic idea is to push all the editors, the for the authors and agents check if it already exist in the previously composed array.

var arrobj = [
  {'id': 1, 'editors': 'Andrew||Maria', 'authors': 'Dorian||Gabi', 'agents': 'Bob||Peter'},
  {'id': 2, 'editors': 'Dorian||Guybrush', 'authors': 'Peter||Frodo', 'agents': 'Dorian||Otto'},
  {'id': 3, 'editors': 'Klaus||Otmar', 'authors': 'Jordan||Morgan', 'agents': 'Jordan||Peter'},
    ];
    
function getInvolved(arr) {
  return arr.map(function(el) {
    var editors = el.editors.split('||');
    var authors = el.authors.split('||');
    var agents = el.agents.split('||'); 
    var involved = editors.map(function(editor) {
      return editor += ' (editor)';
    });
    
    authors.forEach(function(author) { 
      if(editors.indexOf(author) === -1) {
        involved.push(author + ' (author)');
      } else {
        involved = involved.map(function(inv) {
          if(inv.indexOf(author) > -1) {
            inv += '(author)';
          }
          return inv;
        });
      }
    });
    
    agents.forEach(function(agent) { 
      if(editors.indexOf(agent) === -1 && authors.indexOf(agent) === -1) {
        involved.push(agent + ' (agent)');
      } else {
        involved = involved.map(function(inv) {
          if(inv.indexOf(agent) > -1) {
            inv += '(agent)';
          }
          return inv;
        });
      }
    });
    el.involved = involved.join('||'); 
    return el;
  });
}

console.log(getInvolved(arrobj));

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386604

You need to collect all names with their job types and return a grouped result.

 const
     getInvolved = o => {
         const
             jobs = ['editor', 'author', 'agent'],
             names = jobs.reduce((r, k) => {
                 (o[k + 's'] || '').split('||').forEach(v => (r[v] ??= []).push(k));
                 return r;
             }, {});

         return Object.entries(names).map(([k, v]) => `${k} (${v.join(', ')})`).join(' || ')
     },
     array = [{ id: 1, editors: 'Andrew||Maria', authors: 'Dorian||Gabi', agents: 'Bob||Peter' }, { id: 2, editors: 'Dorian||Guybrush', author: 'Peter||Frodo', agents: 'Dorian||Otto' }, { id: 3, editors: 'Klaus||Otmar', authors: 'Jordan||Morgan', agents: 'Jordan||Peter' }],
     result = array.map(o => ({ ...o, involved: getInvolved(o) }));

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

Upvotes: 1

Related Questions