Map an object of properties where some properties do not exist - javascript

I currently am utilising Array.map to create a new object containing some data:
const bookList = [{
name: "Foo",
id: "1234",
quantity: 5,
}];
function mapBooks(bookList) {
return {
eventName: "ping",
data: {
list: {
books:
bookList.map(
({name, id, quantity }) => ({ name, id, quantity})
)
}
}
};
}
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
quantity: 5,
}
}
},
}
This is fine in this example, but what happens when one of the items is not in the provided data?
const bookList = [{
name: "Foo",
id: "1234",
}];
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
quantity: undefined,
}
}
},
}
How can I adjust my map function to simply not return any undefined values? For example I would prefer a result like this:
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
// quantity is simply not included
}
}
},
}

I don't know if i understand the question correctly but you could something like this:
const bookList = {
name: "Foo",
id: "1234",
quantity: 5,
};
function mapBooks(bookList) {
return {
eventName: "ping",
data: {
list: {
books:
bookList.map(
(book) => ({ ...book})
)
}
}
};
}
With the power of destructuring you will only fulled the present option of the object

Related

Find synced users from two arrays based on id and return array A with one column from array B

i am trying to find a list of synced users between two arrays (objArray1 & objArray2) and return the data from objArray1 along with 'aid' in Objarray2. I have the below code working, but i am trying to see if can return 'aid' from objArray2 as well in the below format.
Code sample below
// SystemA
const objArray1 = [
{ id: "1", name: "John" },
{ id: "2", name: "Jack" },
{ id: "3", name: "Sam" },
{ id: "4", name: "Bill" },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam" }, aid:"uuud29029303"
{ id: "5", name: "Bob" }, aid:"uuud29435454"
];
const array1IDs = objArray1.map((item) => {
return item.id
})
// Result: array1IDs = ["1", "2", "3", "4"];
const array2IDs = objArray2.map((item) => {
return item.id
})
// Result: array2IDs = ["1", "3", "5"];
// FIND SYNCED USERS
// Compare the id value of each item in objArray1 with each item of objArray2
// Return the ones that match.
const syncedUsers = objArray1.filter((item) => {
const found = objArray2.find((element) => element.id === item.id);
return found;
});
Required JSON Format, please note that all matching items should be returned from objArray1, with the exception of 'aid' from objArray2
{
"records": [
{
"id": {aid}, // from objArray2
"fields": {
"Name":{name}, // from objArray1
"sid": {id} // from objArray1
}
]
}
Presented below is one possible way to achieve the desired objective.
Code Snippet
// method to create result as "JSON"
const createJSON = (arr1, arr2) => (
// use "stringify" to transform JavaScript object into JSON
JSON.stringify({
// set-up the "records" prop
records: arr2
.filter( // filter to keep only those that have matching "id"
({ id }) => arr1.some(ob => ob.id === id)
)
.map( // de-structure & rename props to match desired objective
({ id : sid, name : Name, aid: id }) => ({
id,
fields: {Name, sid}
})
)
})
);
// SystemA
const objArray1 = [
{ id: "1", name: "John" },
{ id: "2", name: "Jack" },
{ id: "3", name: "Sam" },
{ id: "4", name: "Bill" },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam", aid:"uuud29029303" },
{ id: "5", name: "Bob", aid:"uuud29435454" },
];
console.log('result as a JSON: ', createJSON(objArray1, objArray2));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
EDIT
Use name and id from array-1. Used restOfArr1Props to account for all other props of array-1 objects to be included.
const createJSON = (arr1, arr2) => (
JSON.stringify({
records: arr1
.filter(
({ id }) => arr2.some(ob => ob.id === id)
)
.map(
({ id : sid, name : Name, ...restOfArr1Props }) => ({
id: arr2.find(a2 => a2.id === sid)?.aid ?? 'missing aid',
fields: {
Name, sid, ...restOfArr1Props
},
})
)
})
);
// SystemA
const objArray1 = [
{ id: "1", name: "John", prop1: 'value11', prop2: 'value12' },
{ id: "2", name: "Jack", prop1: 'value21', prop2: 'value22' },
{ id: "3", name: "Sam", prop1: 'value31', prop2: 'value32' },
{ id: "4", name: "Bill", prop1: 'value41', prop2: 'value42' },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam", aid:"uuud29029303" },
{ id: "5", name: "Bob", aid:"uuud29435454" },
];
console.log('result as a JSON: ', createJSON(objArray1, objArray2));
.as-console-wrapper { max-height: 100% !important; top: 0 }
const objArray1 = [
{ id: '1', name: 'John', type: 'bully' },
{ id: '2', name: 'Jack', type: 'sporty' },
{ id: '3', name: 'Sam', type: 'kind' },
{ id: '4', name: 'Bill', type: 'poliet' },
];
const objArray2 = [
{ id: '1', name: 'John', aid: 'uuud2905555' },
{ id: '3', name: 'Sam', aid: 'uuud29029303' },
{ id: '5', name: 'Bob', aid: 'uuud29435454' },
];
const syncedUsers = { records: [] };
for (let user1 of objArray1) {
const foundUser = objArray2.find(user2 => user2.id === user1.id);
if (foundUser) {
syncedUsers.records.push({
id: foundUser.aid,
fields: { sid: user1.id, name: user1.name, ...user1 },
});
}
}
console.log(JSON.stringify(syncedUsers));

fillter arrays of objects

i have two arrays.
const department = [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
];
const models = [
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }],
},
{
id: '54',
name: 'model2',
departments: [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
],
},
];
i need to render accordions with department names and accordion details with matching models names. My question is how to filter those arrays to get models
We can map through the departments array, and add a models property that equals the models array, but filtered only to the ones that contain a matching department id.
const departments = [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
];
const models = [
{
id: "23",
name: "model1",
departments: [{ id: "1", name: "department1" }],
},
{
id: "54",
name: "model2",
departments: [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
],
},
];
const getDepartmentsWithModels = () => {
return departments.map((department) => {
return {
...department,
models: models.filter((model) => {
const modelDepartmentIds = model.departments.map(({ id }) => id);
return modelDepartmentIds.includes(department.id);
}),
};
});
};
console.log(getDepartmentsWithModels());
// [ { id: '1', name: 'department1', models: [ [Object], [Object] ] },
// { id: '2', name: 'department2', models: [ [Object] ] } ]```
I've built some code, which iterates over the departments. For each department it iterates the models and for each model it checks if the department is within the model departments.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }]
},
{
id: '54',
name: 'model2',
departments: [{ id: '1', name: 'department1' },{ id: '2', name: 'department2' }]
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.forEach(model => {
if (model.departments.find(modelDep => dep.id===modelDep.id)) {
console.log(` Model: ${model.name}`)
}
})
})
If you could change your data objects, then your code could be much smoother.
I've changed your data objects slightly by just reducing the departments in a model to be an array of department id's. This code iterates over the departments. For each department it filters the models and iterates over the filtered models to output them to the console. This is lesser code and provides much better performance.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: ['1']
},
{
id: '54',
name: 'model2',
departments: ['1', '2']
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.filter(model => model.departments.includes(dep.id)).forEach(model => {
console.log(` Model: ${model.name}`)
})
})
There are two solutions.
Using Array.reduce() --> returns an object where the key is department name and value is an array of the names of matching models:
let data1 = models.reduce((res, curr) => {
curr.departments.forEach(dep => {
if (!res[dep.name]) {
res[dep.name] = [curr.name]
} else {
if (!res[dep.name].includes(curr.name)) {
res[dep.name].push(curr.name);
}
}
})
return res;
}, {});
Using map and filter --> returns an array of kind:
[{department: [names of the models]},...]
let data2 = department.map(dep => {
let matchingModels = models.filter(model => {
return model.departments.filter(modDep => {
return modDep.name === dep.name;
}).length > 0;
}).map(mod => {
return mod.name;
});
return {
department: dep.name,
models: matchingModels
}
});

Rename result property in normalizr

Given data like:
{
id: 1,
ownerName: 'bob',
devices: [
{
id: 2
},
{
id: 3
}
]
}
how would I convert it to the following object
{
result: 1,
entities: {
owners: {
1: {
id: 1,
ownerName: 'bob',
deviceIds: [2, 3]
}
},
devices: {
2: {
id: 2
},
3: {
id: 3
}
}
}
}
using normalizr? I can't figure out how to change devices to deviceIds in the returned result...
You can use the Process Strategy for this. It allows you to manipulate your data before it is processed. Simply return a copy of your object with the keys changed from the processStrategy() method.
const Device = schema.Entity('devices');
const Owner = schema.Entity(
'owners',
{
deviceIds: [ Device ]
},
{
processStrategy: value => ({
id: value.id,
ownerName: value.ownerName,
deviceIds: value.devices
})
}
);

Is it possible to only update array state of objects?

I was wondering if there is any documentation or reference that could help me to to understand how to update a state of objects array (Without duplicates).
My state looks like this:
accounts: [
{ name: ‘mike’ },
{ name: ‘tee’ },
{ name: ‘ralf’ },
{ name: ‘candy’ },
{ name: ‘bon’ },
{ name: ‘salm’ },
{ name: ‘shark’ },
{ name: ‘tof’ },
{ name: ‘hulk’ },
{ name: ‘zar’ },
{ name: ‘blake’ },
],
the upcoming array is like this:
accounts: [
{ name: 'mike’, balance: ’1000’},
{ name: 'tee’, balance: ’235345’},
{ name: 'zar’, balance: ’3455’},
{ name: 'candy’, balance: ’567567’},
{ name: 'tee’, balance: ’8767’},
{ name: 'salm', balance: ’234’},
{ name: 'blake', balance: ’134’},
],
So the updated state on setState will look like this:
accounts: [
{ name: 'mike’, balance: ’1000’},
{ name: 'tee’, balance: ’235345’},
{ name: ‘ralf’ },
{ name: 'candy’, balance: ’567567’},
{ name: ‘bon’ },
{ name: 'salm', balance: ’234’},
{ name: ‘shark’ },
{ name: ‘tof’ },
{ name: ‘hulk’ },
{ name: 'zar’, balance: ’3455’},
{ name: 'blake', balance: ’134’},
],
I have tried with prevState.accounts.concat(accounts) but it only adds duplicates.
Find object based on condition and then update values where use Array#find method to find the element and Object.assign method to copy values to an existing object.
accounts.forEach( o => {
let oldObj = prevState.accounts.find(o1 => o1.name === o.name);
Object.assign(oldObj, o)
})
Final code would be like :
this.setState(prevState => {
newAccounts.forEach(o => {
let oldObj = prevState.accounts.find(o1 => o1.name === o.name);
Object.assign(oldObj, o)
})
return prevState.accounts
});
Oneliner solution by creating a new array.
this.setState(prevState => newAccounts.map(o => Object.assign(prevState.accounts.find(o1 => o1.name === o.name), o)));
// if you don't want to mutate original object in previous state then
this.setState(prevState => newAccounts.map(o => Object.assign({}, prevState.accounts.find(o1 => o1.name === o.name), o)));
// or with ES6 spread syntax
this.setState(prevState => newAccounts.map(o => ({ ...prevState.accounts.find(o1 => o1.name === o.name), ...o }))));
If your new state accounts is always going to be subset of the previous state accounts value. You can use something like this
this.state = {
accounts : [
{ name: 'mike' },
{ name: 'tee' },
{ name: 'ralf' },
{ name: 'candy' },
{ name: 'bon' },
{ name: 'salm' },
{ name: 'shark' },
{ name: 'tof' },
{ name: 'hulk' },
{ name: 'zar' },
{ name: 'blake' },
]
}
const newAccounts = [
{ name: 'mike', balance: 1000},
{ name: 'tee', balance: 235345},
{ name: 'zar', balance: 3455},
{ name: 'candy', balance: 567567},
{ name: 'tee', balance: 8767},
{ name: 'salm', balance: 234},
{ name: 'blake', balance: 134},
]
this.setState({accounts: this.state.accounts.map (x => ({...x, ...newAccounts.find(y => y.name === x.name)}))});
You can use Array.find to find the values of old state accounts in new accounts and then use ... or Object.assign to merge properties.

Javascript filtering nested arrays

I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}

Categories

Resources