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.

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Apr 29, 2021 at 2:22
\$\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\$ Commented Apr 29, 2021 at 22:23

1 Answer 1

1
\$\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)

answered Apr 29, 2021 at 5:05
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.