This question already has answers here:
How can I perform an inner join with two object arrays in JavaScript?
(7 answers)
Closed 1 year ago.
My database gives me the following data:
var responses = [
{ comment: 'Yes', uid: '5hg' },
{ comment: 'Maybe', uid: 'f1' },
{ comment: 'No', uid: 'b1k2' },
{ comment: 'Yes', uid: '6t2' },
{ comment: 'Yes', uid: 'hd1' },
];
var users = [
{ name: 'Trevor Hansen', group: 'Group 1', uid: 'f1' },
{ name: 'Britta Holt', group: 'Group 2', uid: '5hg' },
{ name: 'Jane Smith ', group: 'Group 2', uid: '6t2' },
{ name: 'Sandra Adams', group: 'Group 1', uid: 'c92c' },
{ name: 'Ali Connors', group: 'Group 1', uid: 'b2' },
{ name: 'John Smith', group: 'Group 2', uid: '9l2' },
{ name: 'Sandra Williams', group: 'Group 2', uid: 'hd1' },
{ name: 'Tucker Smith', group: 'Group 1', uid: 'b1k2' },
];
Because I store all of my user data only in users[] for different purposes I need to add some information to responses[] about the user (like their name and group). The uid is unique and can be used to match the data to a user.
Obviously there are less responses than users in responses[]. This should not affect my function and is an expected behavior.
This is the desired output:
var output = [
{ comment: 'Yes', uid: '5hg', name: 'Britta Holt', group: 'Group 2' },
{ comment: 'Maybe', uid: 'f1', name: 'Trevor Hansen', group: 'Group 1' },
{ comment: 'No', uid: 'b1k2', name: 'Tucker Smith', group: 'Group 1' },
{ comment: 'Yes', uid: '6t2', name: 'Jane Smith ', group: 'Group 2' },
{ comment: 'Yes', uid: 'hd1', name: 'Sandra Williams', group: 'Group 2' },
];
How can this be done? Any help is appreciated!
you can try for example:
const output = responses.map(response => {
const user = users.find(u => u.uid === response.uid);
return {...response, ...user}
})
or single liner:
const output = responses.map(response => ({...response, ...users.find(u => u.uid === response.uid)}));
Related
I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`
I have an array of appointments objects:
let appointments = [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"},
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"},
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"},
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
]
my goal is to create a new array based on the days of the appointments and place them inside, like so:
{ days:
{ 2022-01-20: [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"}
]},
{ 2022-01-21: [
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"}
]},
{ 2022-01-22: [
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"}
]},
{ 2022-01-23: []},
{ 2022-01-24: []},
{ 2022-01-25: [
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
]},
}
The first 10 characters of 'date' could become the new values (excluding duplicates) and inside them there should be the proper appointments, as they are in the source - only organized by the days.
Another feature that I'm trying to make is inserting empty days between the active days (example in the second code)
Thanks for your help
const appointments = [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"},
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"},
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"},
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
];
const appointmentsByDate = appointments.reduce(
(acc, appointment) => {
// split the date string and store index 0 as date variable.
const [date] = appointment.date.split('T');
return {
...acc,
// overwrite or add date key to the accumulator.
// if the date key already exists, spread the existing value into the new value
[date]: [...(acc[date] || []), appointment],
};
},
{} // starting accumulator (acc) value
);
console.log(appointmentsByDate);
I have three state, two of them have data from different api and the third state need to merge those two states based on IDs. So, the third state should have every data that state1 have and state2 don't have, and every data that state2 have and state1 don't have.
Api1:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
}
}
Api2:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
lastName: 'Second 2'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3'
}
}
New Array should be (lastName = name + lastName :
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second Test'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
lastName: 'Second 2 Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1 Test 2'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3 Test 3'
}
Fetched Data:
const [state1, setState1] = useState([]);
const [state2, setState2] = useState([]);
const [mergeStates, setMergeStates] = useState([]);
useEffects(() => {
fetch("api1")
.then(data =>{
state1(data);
})
fetch("api2")
.then(data =>{
state2(data);
})
}, []);
useEffects(() => {
// Here I want to merge the responses based on IDs
const lastName = companies.map((response) => ({
name: response.name,
lastName: `${response.name} - ${response.lastName}`
}));
setMergeState(lastName);
}, [state1, state2]);
So, the api2 has lastName that api1 doesn't have. So, the mergedStates need to include that.
based on your quetion to merge the two state, you need to iterate over a state either 1 or 2, while merging them.
useEffects(() => {
const keys = Object.keys(state1);
mergedData = keys.map(key => {
// key will be 0,1,2 as with your quetion
return {
...state1[key],
...state2[key],
fullName: state1[key].name + state2[key].lastName
};
});
}, [state1, state2]);
I have a node.js mysql query:
connection.query("SELECT id AS id, name AS label, status AS status from table;", function(err, rows) {
...
});
The result I'm getting back locks like this:
console.log(getBody)
[ { id: '1',
label: 'Name 01',
status: 'ACTIVE' },
{ id: '2',
label: 'Name 02',
status: 'INACTIVE' },
{ id: '3',
label: 'Name 03',
status: 'ACTIVE' },
{ id: '4',
label: 'Name 04',
status: 'ACTIVE' }];
To further cosume the result ... I need an additional paremeter 'type' with with a fixed value in the array. So result should look like this:
[ { id: '1',
label: 'Name 01',
status: 'ACTIVE',
type: 'ABC' },
{ id: '2',
label: 'Name 02',
status: 'INACTIVE',
type: 'ABC' },
{ id: '3',
label: 'Name 03',
status: 'ACTIVE',
type: 'ABC' },
{ id: '4',
label: 'Name 04',
status: 'ACTIVE',
type: 'ABC' }];
What's the fastest/best way to do this? Looping over the array? How should it look like?
Use the map method:
const newArray = array1.map(element => element = {...element, ...{type: 'ABC'}});
console.log(array1); // array1 won't be changed
console.log(newArray);
Or forEach, this will modify your array:
array1.forEach(element => element.type = 'ABC');
console.log(array1);
var arr = [
{id: '1',label: 'Name 01',status: 'ACTIVE'},
{id: '2',label: 'Name 02',status: 'INACTIVE'},
{id: '3',label: 'Name 03',status: 'ACTIVE'},
{id: '4',label: 'Name 04',status: 'ACTIVE'}];
var new_arr = arr.map(function(el) {
var o = Object.assign({}, el);
o. type = 'ABC';
return o;
})
console.log(arr);
console.log(new_arr);
I have an array of objects, that looks like this:
data = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'},
]
},
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
How can I iterate over each object's departments array and create new arrays where I would have employees sorted by departments, so that the end result would like this:
operations = [
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
marketing = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
What would be the way to create dynamically this kind of arrays?
Update
I have tried to come up with a solution using the suggestion from the answer, where I would dynamically create an array with department objects that would have an array of employees:
const isInDepartment = departmentToCheck => employer => employer.departments.find(department => department.slug == departmentToCheck);
var departments = [];
function check(departments, name) {
return departments.some(object => name === object.department);
}
employees.forEach((employee) => {
employee.departments.forEach((department) => {
let found = check(departments, department.slug);
if (!found) {
departments.push({ department: department.slug });
}
});
});
departments.forEach((department) => {
// push an array of employees to each department
//employees.filter(isInDepartment(department));
});
But, I don't know how can I push the array of employees to the object in the array that I am looping at the end?
This is the fiddle.
How about this? I use Array.protoype.filter operation, and I use a higher-order function (in this case a function that returns a function) to create the predicate (function that returns a boolean) that will check whether an employee is in a specific department. I added some (hopefully) clarifying comments in the code.
Edit: with the new code and context you provided this JSFiddle demo shows how it would work together.
const employees = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'}
]
},
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'}
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'}
]
}
];
// given a department, this returns a function that checks
// whether an employee is in the specified department
// NOTE: the "find" returns the found object (truthy)
// or undefined (falsy) if no match was found.
const isInDepartment =
departmentToCheck =>
employee => employee.departments.find(dep => dep.name == departmentToCheck);
const employeesInMarketing = employees.filter(isInDepartment('Marketing'));
const employeesInOperations = employees.filter(isInDepartment('Operations'));
console.log('Employees in marketing', employeesInMarketing);
console.log('Employees in operations', employeesInOperations);