I have a working program that satisfies the conditions below but I'm wondering if there is a more efficient solution. Currently, I apply 4 different Javascript array method transformations which results in returning a new array for each transformation for a total of 4 new arrays. Can these transformations be combined to only create 1 additional array instead of 4 new arrays? I could chain together the calls but I was thinking it might be possible to combine them all into reduce() method but I'm not sure how that would look or if there is some better solution.
The criteria that needs to be satisfied:
Only include employees from the Google organization but allow this to be passed as an input parameter
Last names should be unique (no duplicate last names)
Employees should be sorted by ID (ascending)
Each employee should have an added property called fullName that is a combination of first and last names, separated by a space
const GOOGLE_ORG = 'Google';
const employees = [
{
id: 3,
firstName: 'John',
lastName: 'Doe',
organization: 'Google',
},
{
id: 7,
firstName: 'Jake',
lastName: 'Smith',
organization: 'Google',
},
{
id: 1,
firstName: 'Jane',
lastName: 'Doe',
organization: 'Google',
},
{
id: 2,
firstName: 'Vanessa',
lastName: 'Smith',
organization: 'Meta',
},
{
id: 5,
firstName: 'Sarah',
lastName: 'Hernandez',
organization: 'Meta',
},
{
id: 8,
firstName: 'Jessica',
lastName: 'Morales',
organization: 'Google',
},
{
id: 4,
firstName: 'Paul',
lastName: 'Stark',
organization: 'Google',
},
{
id: 6,
firstName: 'Peter',
lastName: 'Brown',
organization: 'Meta',
},
];
const transformArray = (org) => {
const filteredByOrg = employees.filter((employee) => employee.organization === org);
const addedFullName = filteredByOrg.map((employee) => ({
...employee,
fullName: employee.firstName + ' ' + employee.lastName,
}));
const uniqueByLastName = [...addedFullName.reduce((map, obj) => map.set(obj.lastName, obj), new Map()).values()];
return uniqueByLastName.sort((a, b) => a.id - b.id);
};
transformArray(GOOGLE_ORG);
Yes, you can use Array.prototype.reduce to minimize creating arrays and the number of iterations.
const GOOGLE_ORG = 'Google';
const employees = [
{
id: 3,
firstName: 'John',
lastName: 'Doe',
organization: 'Google',
},
{
id: 7,
firstName: 'Jake',
lastName: 'Smith',
organization: 'Google',
},
{
id: 1,
firstName: 'Jane',
lastName: 'Doe',
organization: 'Google',
},
{
id: 2,
firstName: 'Vanessa',
lastName: 'Smith',
organization: 'Meta',
},
{
id: 5,
firstName: 'Sarah',
lastName: 'Hernandez',
organization: 'Meta',
},
{
id: 8,
firstName: 'Jessica',
lastName: 'Morales',
organization: 'Google',
},
{
id: 4,
firstName: 'Paul',
lastName: 'Stark',
organization: 'Google',
},
{
id: 6,
firstName: 'Peter',
lastName: 'Brown',
organization: 'Meta',
},
];
const transformArray = (org) => {
return (employees.reduce((lastNames => ((acc, curr) => {
if(!lastNames.has(curr.lastName) && curr.organization === org) {
lastNames.add(curr.lastName)
acc.push({...curr, fullName: `${curr.firstName} ${curr.lastName}`})
}
return acc;
}))(new Set()), []).sort((a, b) => a.id - b.id));
};
console.log(transformArray(GOOGLE_ORG));
You could chain all operations.
Actually it returns a different reuslt by filtering unique lastnames and sorting after or by sorting first and then filtering.
An example of a closure:
Take this line for filtering with a Set:
.filter((s => ({ lastName }) => !s.has(lastName) && s.add(lastName))(new Set))
A closure takes variables into the scope of the calling function and returns another function. This function has access to the variable.
Let's have a look with a different formatting:
.filter(
(s => ({ lastName }) => !s.has(lastName) && s.add(lastName))
(new Set)
)
Here, the function is called with new Set and this function is returned
({ lastName }) => !s.has(lastName) && s.add(lastName)
as callback for the filter method.
const
GOOGLE_ORG = 'Google',
employees = [{ id: 3, firstName: 'John', lastName: 'Doe', organization: 'Google' }, { id: 7, firstName: 'Jake', lastName: 'Smith', organization: 'Google' }, { id: 1, firstName: 'Jane', lastName: 'Doe', organization: 'Google' }, { id: 2, firstName: 'Vanessa', lastName: 'Smith', organization: 'Meta' }, { id: 5, firstName: 'Sarah', lastName: 'Hernandez', organization: 'Meta' }, { id: 8, firstName: 'Jessica', lastName: 'Morales', organization: 'Google' }, { id: 4, firstName: 'Paul', lastName: 'Stark', organization: 'Google' }, { id: 6, firstName: 'Peter', lastName: 'Brown', organization: 'Meta' }],
result = employees
.filter(({ organization }) => organization === GOOGLE_ORG)
.filter((s => ({ lastName }) => !s.has(lastName) && s.add(lastName))(new Set))
.sort((a, b) => a.id - b.id)
.map(o => ({ ...o, fullName: o.firstName + o.lastName }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have following array that needs to be sorted with respect to search text 'John'.
{id: 1, firstName: 'User', lastName: 'John', nickName: 'Smith'},
{id: 2, firstName: 'Test', lastName: 'John', nickName: 'Andrew'},
{id: 3, firstName: 'Test', lastName: 'Zch', nickName: 'John'},
{id: 4, firstName: 'Test', lastName: 'Mason', nickName: 'John'},
{id: 5, firstName: 'John', lastName: 'Doe'},
];
Expected Output:
Array should be first sorted with nickName (with search text) then lastName(with search text). If nickName is not present then it should sorted with respect to firstName(with search text) with ASC sorting order.
Note: It should consider search text word as 'John'
This sort resembles like Search with Sort in your mobile's contact app
[
// sort with nickName as higher relevance considering search text as John
{id: 4, firstName: 'Test', lastName: 'Mason', nickName: 'John'},
{id: 3, firstName: 'Test', lastName: 'Zch', nickName: 'John'},
// sort with lastName considering search text
{id: 2, firstName: 'Test', lastName: 'John', nickName: 'Andrew'},
{id: 1, firstName: 'User', lastName: 'John', nickName: 'Smith'},
// sort with firstName as nickName is null
{id: 5, firstName: 'John', lastName: 'Doe'},
];
I tried localeMethod
function sortByLocale(user1, user2) {
var sortByNickName = user1.nickName.toLowerCase().localeCompare(user2.nickName.toLowerCase());
var sortByLastName = user1.lastName.toLowerCase().localeCompare(user2.lastName.toLowerCase());
return sortByNickName || sortByLastName;
}
But the result is not considering search text while sorting.
One approach, I can see is creating three different arrays and sort them and combined those sorted array
Any helps would be appreciated.
Edit: Not considering the non-matched object with search text value
Just add first search by name
function checkSearch (value) {
return (value.nickName === 'John') * -3 ||
(value.lastName === 'John') * -2 ||
(value.firstName === 'John') * -1 ||
0
}
function sortByLocale(user1, user2) {
var sortBySearch = checkSearch(user1) - checkSearch(user2)
var sortByNickName = (user1.nickName || '').toLowerCase().localeCompare((user2.nickName || '').toLowerCase());
var sortByLastName = user1.lastName.toLowerCase().localeCompare(user2.lastName.toLowerCase());
return sortBySearch || sortByNickName || sortByLastName;
}
You could take two iterations for the wanted order
one for the wanted string
for the order of the rest
var data = [{ id: 1, firstName: 'User', lastName: 'John', nickName: 'Smith' },
{ id: 2, firstName: 'Test', lastName: 'John', nickName: 'Andrew' },
{ id: 3, firstName: 'Test', lastName: 'Zch', nickName: 'John' },
{ id: 4, firstName: 'Test', lastName: 'Mason', nickName: 'John' },
{ id: 5, firstName: 'John', lastName: 'Doe' }
],
search = 'john',
check = (s => (o, k) => (o[k] || '').toLowerCase() === search)(search),
keys = ['nickName', 'lastName', 'firstName'];
data.sort((a, b) => {
const
fns = [
k => d = check(b, k) - check(a, k),
k => d = (a[k] || '').localeCompare(b[k] || '')
];
let d = 0;
fns.some(fn => keys.some(fn));
return d;
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am looking to combine these two arrays into a single one. I want any id information that is the same to be filtered so that it only appears once, making it a simple list of name, age, occupation, and address.
I have tried simply concating the info, using splice, using filter... but I just cant seem to get the right answer.
var a = [{
id: 'aBcDeFgH',
firstName: 'Juan',
lastName: 'Doe',
age: 32
},
{
id: 'zYxWvUt',
firstName: 'Alex',
lastName: 'Smith',
age: 24
}]
var b = [{
id: 'aBcDeFgH',
occupation: 'architect',
address: {
street: '123 Main St',
city: 'CityTown',
Country: 'USA'
}
},
{
id: 'zYxWvUt',
occupation: 'receptionist',
address: {
street: '555 Ocean Ave',
city: 'Beach City',
Country: 'USA'
}
}]
I always end up with a single list after the concat, but I cant find out how to filter the same info.
Sounds like you need to merge each item of each array together - and that they're both in the same order, in which case you could do:
const newList = []
a.forEach((item, index) => {
newList.push({
...item,
...b[index]
})
})
console.log(newList)
You can make an object from first array a whole keys will be id of each object. Then use map() on b and return object having all props.
var a = [{
id: 'aBcDeFgH',
firstName: 'Juan',
lastName: 'Doe',
age: 32
},
{
id: 'zYxWvUt',
firstName: 'Alex',
lastName: 'Smith',
age: 24
}]
var b = [{
id: 'aBcDeFgH',
occupation: 'architect',
address: {
street: '123 Main St',
city: 'CityTown',
Country: 'USA'
}
},
{
id: 'zYxWvUt',
occupation: 'receptionist',
address: {
street: '555 Ocean Ave',
city: 'Beach City',
Country: 'USA'
}
}]
let obj = a.reduce((ac,a) => (ac[a.id] = a,ac),{});
let res = b.map(x => ({...x,...obj[x.id]}));
console.log(res)
The following will reconstruct the array in the same order as a but the function doesn't depend on b being in the same order.
var a = [{id:"aBcDeFgH",firstName:"Juan",lastName:"Doe",age:32},{id:"zYxWvUt",firstName:"Alex",lastName:"Smith",age:24}],
b = [{id:"aBcDeFgH",occupation:"architect",address:{street:"123 Main St",city:"CityTown",Country:"USA"}},{id:"zYxWvUt",occupation:"receptionist",address:{street:"555 Ocean Ave",city:"Beach City",Country:"USA"}}];
let res = a.reduce((a,c) => {a.push({...c, ...b.find(v => v.id == c.id)}); return a;},[])
console.log(res)
And as a more performant solution, that instead of using find for every look-up uses a map-like object for our second array so we just have to insert from it for our result via O(1) look-ups.
So instead of O(n²) we now have O(n):
var a = [{id:"aBcDeFgH",firstName:"Juan",lastName:"Doe",age:32},{id:"zYxWvUt",firstName:"Alex",lastName:"Smith",age:24}],
b = [{id:"aBcDeFgH",occupation:"architect",address:{street:"123 Main St",city:"CityTown",Country:"USA"}},{id:"zYxWvUt",occupation:"receptionist",address:{street:"555 Ocean Ave",city:"Beach City",Country:"USA"}}];
let tmp = b.reduce((a,c) => {a[c.id] = c; return a},{}),
res = a.reduce((a,c) => {a.push({...c, ...tmp[c.id]}); return a;},[]);
console.log(res)
If the id is the key to compare,
const concatListById = (base, target) => base.reduce((acc, c) => {
const matched = target.find(e => e.id === c.id);
let el = c;
if (matched) el = { ...matched, ...c };
acc.push(el);
return acc;
}, []);
console.log(concatListById(a, b));
If there is an assumption that id sequence of each array is the same,
const justMergeArray = (base, target) =>
base.map((e, idx) => ({ ...e, ...target[idx] }));
console.log(justMergeArray(a, b));
You can use reduce and compare if id match just push into same object and add this to an array
var a = [{
id: 'aBcDeFgH',
firstName: 'Juan',
lastName: 'Doe',
age: 32
},
{
id: 'zYxWvUt',
firstName: 'Alex',
lastName: 'Smith',
age: 24
}
]
var b = [{
id: 'aBcDeFgH',
occupation: 'architect',
address: {
street: '123 Main St',
city: 'CityTown',
Country: 'USA'
}
},
{
id: 'zYxWvUt',
occupation: 'receptionist',
address: {
street: '555 Ocean Ave',
city: 'Beach City',
Country: 'USA'
}
}
]
const res = a.reduce((all, acc, index) => {
if (acc.id === b[index].id) {
all.push({
...acc,
...b[index]
});
}
return all;
}, []);
console.log(res);
Try this:
var a = [{
id: 'aBcDeFgH',
firstName: 'Juan',
lastName: 'Doe',
age: 32
},
{
id: 'zYxWvUt',
firstName: 'Alex',
lastName: 'Smith',
age: 24
}]
var b = [{
id: 'aBcDeFgH',
occupation: 'architect',
address: {
street: '123 Main St',
city: 'CityTown',
Country: 'USA'
}
},
{
id: 'zYxWvUt',
occupation: 'receptionist',
address: {
street: '555 Ocean Ave',
city: 'Beach City',
Country: 'USA'
}
}];
const newA = a.reduce((acc, ele) => (acc[ele.id] = ele, ele),{});
const result = b.map(ele=> ({...newA[ele.id],...ele}));
console.log(result);
I have 2 arrays of objects
var arr1 = [{id: "145", firstname: "dave", lastname: "jones"},
{id: "135", firstname: "mike",lastname: "williams"},
{id: "148", firstname: "bob",lastname: "michaels"}];
var arr2 = [{id: "146", firstname: "dave", lastname: "jones"},
{id: "135", firstname: "mike", lastname: "williams"},
{id: "148", firstname: "bob", lastname: "michaels"}];
I want to find the objects where the id exists in only one of the arrays and either log the object to the console or push the object to a new array.
Therefore I want to end up with
var arr1 = [{id: "145", firstname: "dave", lastname: "jones"}]
var arr2 = [{id: "146", firstname: "dave", lastname: "jones"}]
I tried using a forEach loop and splicing matching id's out of the array
arr1.forEach(function(element1, index1) {
let arr1Id = element1.id;
arr2.forEach(function(element2, index2) {
if (arr1Id === element2.id) {
arr1.splice(element1, index1)
arr2.splice(element2, index2)
};
});
});
console.log(arr1);
console.log(arr2);
But I ended up with
arr1
[ { id: '135', firstname: 'mike', lastname: 'williams' },
{ id: '148', firstname: 'bob', lastname: 'michaels' } ]
arr2
[ { id: '135', firstname: 'mike', lastname: 'williams' },
{ id: '148', firstname: 'bob', lastname: 'michaels' } ]
You could take a Set for every array's id and filter the other array by checking the existence.
var array1 = [{ id: "145", firstname: "dave", lastname: "jones" }, { id: "135", firstname: "mike", lastname: "williams" }, { id: "148", firstname: "bob", lastname: "michaels" }],
array2 = [{ id: "146", firstname: "dave", lastname: "jones" }, { id: "135", firstname: "mike", lastname: "williams" }, { id: "148", firstname: "bob", lastname: "michaels" }],
set1 = new Set(array1.map(({ id }) => id)),
set2 = new Set(array2.map(({ id }) => id)),
result1 = array1.filter(({ id }) => !set2.has(id)),
result2 = array2.filter(({ id }) => !set1.has(id));
console.log(result1);
console.log(result2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Just use !arr.some() inside a Array.prototype.filter():
const arr1 = [{id: "145", firstname: "dave", lastname: "jones"},{id: "135", firstname: "mike",lastname: "williams"},{id: "148", firstname: "bob",lastname: "michaels"}],
arr2 = [{id: "146", firstname: "dave", lastname: "jones"},{id: "135", firstname: "mike", lastname: "williams"},{id: "148", firstname: "bob", lastname: "michaels"}],
newArr1 = arr1.filter(x => !arr2.some(y => y.id === x.id)),
newArr2 = arr2.filter(x => !arr1.some(y => y.id === x.id));
console.log(newArr1, newArr2);
Hello please try using combination of filter and findindex like the below snippet and let me know.
var arr1 = [{id: "145", firstname: "dave", lastname: "jones"},
{id: "135", firstname: "mike",lastname: "williams"},
{id: "148", firstname: "bob",lastname: "michaels"}];
var arr2 = [{id: "146", firstname: "dave", lastname: "jones"},
{id: "135", firstname: "mike", lastname: "williams"},
{id: "148", firstname: "bob", lastname: "michaels"}];
let unmatchedArr1 = arr1.filter(element => {
let targetIndex = arr2.findIndex(e => element.id === e.id);
return targetIndex >= 0 ? false : true;
})
let unmatchedArr2 = arr2.filter(element => {
let targetIndex = arr1.findIndex(e => element.id === e.id);
return targetIndex >= 0 ? false : true;
})
console.log(unmatchedArr1);
console.log(unmatchedArr2);
Hello I have kind of complicated iteration to be done over an array of objects. I have array like this:
[
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Jacob', lastName: 'Smith', dob: '1991-08-21' },
{ name: 'Ann', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Nansen', dob: '1983-01-01' },
{ name: 'Jacob', lastName: 'Smith', dob: '1985-06-15' },
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Smith', dob: '2010-11-29' },
]
I would like to add count property to each object that counts objects with same name and surname... So it should be now:
[
{ name: 'Jacob', lastName: 'Smith', count: 4 },
{ name: 'Ann', lastName: 'Smith', count: 2 },
{ name: 'Ann', lastName: 'Nansen', count: 1' },
]
You can use Array.reduce and Object.values
Convert array in an object with key as name and last name combination with value being the resulting object.
From the object, get all values as the final result
let arr = [{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Jacob', lastName: 'Smith', dob: '1991-08-21' },{ name: 'Ann', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Ann', lastName: 'Nansen', dob: '1983-01-01' },{ name: 'Jacob', lastName: 'Smith', dob: '1985-06-15' },{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Ann', lastName: 'Smith', dob: '2010-11-29' }];
let result = Object.values(arr.reduce((a,{name, lastName}) => {
let key = `${name}_${lastName}`;
a[key] = a[key] || {name, lastName, count : 0};
a[key].count++;
return a;
}, {}));
console.log(result);
const hash = [];
for(const { name, lastName } of persons) {
const key = name + "/" + lastName;
if(!hash[key]) hash[key] = {
name,
lastName,
count: 0,
};
hash[key].count++;
}
const result = Object.values(hash);
You could use JSON.stringify to combine name and last name in a safe way. I like using a Map to group the records with the same keys together:
const data = [{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Jacob', lastName: 'Smith', dob: '1991-08-21' },{ name: 'Ann', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Ann', lastName: 'Nansen', dob: '1983-01-01' },{ name: 'Jacob', lastName: 'Smith', dob: '1985-06-15' },{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },{ name: 'Ann', lastName: 'Smith', dob: '2010-11-29' }];
const keyed = data.map(o => [JSON.stringify([o.name, o.lastName]), o]);
const map = new Map(keyed.map(([key, {name, lastName}]) =>
[key, {name, lastName, count: 0}]));
keyed.forEach(([key, o]) => map.get(key).count++);
const result = Array.from(map.values());
console.log(result);
let arr=[
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Jacob', lastName: 'Smith', dob: '1991-08-21' },
{ name: 'Ann', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Nansen', dob: '1983-01-01' },
{ name: 'Jacob', lastName: 'Smith', dob: '1985-06-15' },
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Smith', dob: '2010-11-29' },
];
let outerArr=[];
for(arrValue of arr)
{
delete arrValue.dob
let index=outerArr.findIndex(item=> item.name==arrValue.name &&
item.lastName==arrValue.lastName);
if(index==-1)
{
let arrFind=arr.filter(item=> item.name==arrValue.name &&
item.lastName==arrValue.lastName)
arrValue.count=arrFind.length
outerArr.push(arrValue)
}
}
console.log('result',outerArr)
You can achieve this by reducing the original Array.
As you iterate through the people you can check if they have already been "grouped" using Array.some - if they haven't, push your built person Object to the previously returned Array.
const getInstances = ({ name, lastName }, data) => data.filter(d => d.name === name && d.lastName === lastName).length
const people = [
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Jacob', lastName: 'Smith', dob: '1991-08-21' },
{ name: 'Ann', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Nansen', dob: '1983-01-01' },
{ name: 'Jacob', lastName: 'Smith', dob: '1985-06-15' },
{ name: 'Jacob', lastName: 'Smith', dob: '1995-11-29' },
{ name: 'Ann', lastName: 'Smith', dob: '2010-11-29' },
]
const groupedPeople = people.reduce((group, person, i, people) => {
const alreadyBeenGrouped = group.some(({ name, lastName }) => name === person.name && lastName === person.lastName)
if (!alreadyBeenGrouped) {
group.push({
name: person.name,
lastName: person.lastName,
count: getInstances(person, people)
})
}
return group
}, [])
console.log(groupedPeople)
I'd like create json with this structure inside the cycle:
{ id_foto:
[ { firstName: 37, lastName: 'Doe' },
{ firstName: 'Anna', lastName: 'Smith' },
{ firstName: 'Peter', lastName: 'Jones' } ] }
I wish it were a variable id_foto
so that:
if (id_foto == n.foto_moderata) {
// add new { firstName: 'Anna', lastName: 'Smith' }
} else {
// create new "node" like
{ id_foto(NEW INDEX):
[ { firstName: 37, lastName: 'Doe' },] }
}
The Final result like:
{ 10:
[ { firstName: 37, lastName: 'Doe' },
{ firstName: 'Anna', lastName: 'Smith' },
{ firstName: 'Peter', lastName: 'Jones' } ]
11:
[ { firstName: fff, lastName: 'fff' },
{ firstName: 'fff', lastName: 'fff' } ]
}
Then take all user of 11 index
One way to achieve a sequential ids for your data is to create:-
1. a place to store the current value of your id,
2. a function to increment and return your serial
You could store the current id value in your data object like so:-
{
seq : 11,
10:
[ { firstName: 37, lastName: 'Doe' },
{ firstName: 'Anna', lastName: 'Smith' },
{ firstName: 'Peter', lastName: 'Jones' } ],
11:
[ { firstName: fff, lastName: 'fff' },
{ firstName: 'fff', lastName: 'fff' } ]
}
and then use the following to increment & return next sequence id
function id_foto() {
return ++your_object.seq;//get,increment and return incremented value
}