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.
-
\$\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ᴇᴌᴀ– Sᴀᴍ Onᴇᴌᴀ ♦2021年04月29日 22:23:42 +00:00Commented Apr 29, 2021 at 22:23
1 Answer 1
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)