1
\$\begingroup\$

I'm trying to solve a problem of how to group name objects. A name has firstName and lastName properties. Need to group the name objects that have identical lastnames and a similar firstName(aka Tom Smith and Thomas Smith should be grouped together). Case doesn't matter. The firstName equivalency is provided by a string separated by ";"'s.

example:

input:

const FIRST_NAME_DICTIONARY =
  "Robert, Bob, Bobby; Liz, Elizabeth, Beth; Tom, Thomas";

const people = [
  { firstName: "robert", lastName: "smith" },
  { firstName: "Liz", lastName: "thomas" },
  { firstName: "robert", lastName: "smith" },
  { firstName: "Thomas", lastName: "hardin" },
  { firstName: "Elizabeth", lastName: "thomas" },
  { firstName: "bob", lastName: "smith" },
  { firstName: "Bobby", lastName: "smith" },
  { firstName: "ryan", lastName: "roberts" },
  { firstName: "bob", lastName: "wallace" },
  { firstName: "bobby", lastName: "smith" },
  { firstName: "beth", lastName: "roberts" },
  { firstName: "beth", lastName: "thomas" },
  { firstName: "Tom", lastName: "hardin" },
];

output:

[
    [
      { firstName: 'robert', lastName: 'smith' },
      { firstName: 'robert', lastName: 'smith' },
      { firstName: 'bob', lastName: 'smith' },
      { firstName: 'Bobby', lastName: 'smith' },
      { firstName: 'bobby', lastName: 'smith' }
    ],
    [
      { firstName: 'Liz', lastName: 'thomas' },
      { firstName: 'Elizabeth', lastName: 'thomas' },
      { firstName: 'beth', lastName: 'thomas' }
    ],
    [
      { firstName: 'Thomas', lastName: 'hardin' },
      { firstName: 'Tom', lastName: 'hardin' }
    ],
    [ { firstName: 'ryan', lastName: 'roberts' } ],
    [ { firstName: 'bob', lastName: 'wallace' } ],
    [ { firstName: 'beth', lastName: 'roberts' } ]
]

Here's what I have now:

const groupDuplicates = (list, dictionary) => {
  const mappedNames = mapDictionary(dictionary);
  const groupByNames = {};
  people.forEach((person) => {
    // gets the id equivalent for firstName or use the firstName it the id DNE
    const firstNameId =
      mappedNames[person?.firstName?.toLowerCase()] ||
      person?.firstName.toLowerCase(); // example 1 or ryan since ryan dne in the dictionary
    const stringifyKey = JSON.stringify([
      firstNameId,
      person.lastName.toLowerCase(),
    ]); // example "[1, smith]"

    // if key exists push to that array, otherwise create the key and then push
    if (groupByNames[stringifyKey]) {
      groupByNames[stringifyKey].push(person);
    } else {
      groupByNames[stringifyKey] = [person];
    }
  });

  // essentially convert object into array
  const keys = Object.keys(groupByNames);
  const groupByNamesArray = [];
  keys.forEach((key) => {
    groupByNamesArray.push(groupByNames[key]);
  });

  return groupByNamesArray;
};

// {
//     robert: 1,
//     bob: 1,
//     bobby: 1,
//     liz: 2,
//     elizabeth: 2,
//     beth: 2,
//     tom: 3,
//     thomas: 3
// }
const mapDictionary = (dictionary) => {
  const nameGroups = dictionary.split(";");
  let nameKey = 1;
  const mapNameToKey = {};
  nameGroups.forEach((nameGroup) => {
    const names = nameGroup.split(",");
    names.forEach((name) => {
      const noSpacesAndLowercase = name.trim().toLowerCase();
      mapNameToKey[noSpacesAndLowercase] = nameKey;
    });
    nameKey += 1;
  });
  return mapNameToKey;
};

Seems to work but is there a better way to accomplish this? I think the JSON.stringify seems a little hacky.

\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review! I rolled back your last edit. After getting an answer you are not allowed to change your code anymore. This is to ensure that answers do not get invalidated and have to hit a moving target. If you have changed your code you can either post it as an answer (if it would constitute a code review) or ask a new question with your changed code (linking back to this one as reference). See the section What should I not do? on What should I do when someone answers my question? for more information \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Apr 29 at 22:23
0
\$\begingroup\$

If you have a simple groupBy function, and group by a string key with format [lastName]|[firstNameKey or firstName], it'd be cleaner. The following is my version of the code:

// FUNCTIONS
const groupBy = (list, key) => {
  return list.reduce((current, value) => {
    (current[key(value)] = current[key(value)] || []).push(value);
    return current;
  }, {});
};

const mapDictionary =
  (str) => str
  .split(';')
  .map((val, idx) => {
    return {
      grp: idx + 1,
      val: val
    };
  })
  .reduce((current, group) => {
    const groupResult = group.val
      .split(',').reduce((gr, name) => {
        gr[name.trim().toLowerCase()] = group.grp;
        return gr;
      }, {});
    return {
      ...current,
      ...groupResult
    };
  }, {});

const groupDuplicate = (people, dictionary) => {
  return Object.values(groupBy(people, (person) => `${person.lastName}|${dictionary[person.firstName.toLowerCase()] || person.firstName}`));
};


// TEST CASE
const FIRST_NAME_DICTIONARY =
  "Robert, Bob, Bobby; Liz, Elizabeth, Beth; Tom, Thomas";

const people = [{
    firstName: "robert",
    lastName: "smith"
  },
  {
    firstName: "Liz",
    lastName: "thomas"
  },
  {
    firstName: "robert",
    lastName: "smith"
  },
  {
    firstName: "Thomas",
    lastName: "hardin"
  },
  {
    firstName: "Elizabeth",
    lastName: "thomas"
  },
  {
    firstName: "bob",
    lastName: "smith"
  },
  {
    firstName: "Bobby",
    lastName: "smith"
  },
  {
    firstName: "ryan",
    lastName: "roberts"
  },
  {
    firstName: "bob",
    lastName: "wallace"
  },
  {
    firstName: "bobby",
    lastName: "smith"
  },
  {
    firstName: "beth",
    lastName: "roberts"
  },
  {
    firstName: "beth",
    lastName: "thomas"
  },
  {
    firstName: "Tom",
    lastName: "hardin"
  },
];

const result = groupDuplicate(people, mapDictionary(FIRST_NAME_DICTIONARY));

// PRINT RESULT
console.log(result)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.