Joining nested individual properties by comma - javascript

I am trying to join the array of objects internal properties with the comma , like as below
const multiSpaceIdentityInformation = spaces?.reduce(
(acc, space) => {
acc.roomName = `${acc.roomName},${space.spaceIdentity.roomName}`;
acc.roomNumber = `${acc.roomNumber},${space.spaceIdentity.roomNumber}`;
acc.storyName = `${acc.storyName},${space.buildingStory?.name}`;
acc.spaceNumber = `${acc.spaceNumber},${space.spaceIdentity.number}`;
acc.spaceName = `${acc.spaceName},${space.spaceIdentity.name}`;
return acc;
},
{
roomName: 'N/A',
roomNumber: 'N/A',
storyName: 'N/A',
spaceNumber:'N/A',
spaceName: 'N/A'
}
);
But somehow, I cannot display the information even I have spaces holding the array of objects.
What I am trying to display is if there is no information, I would like to say 'N/A' as the default option.
I am accessing the above information here
const identityData = [
{ label: 'Room Number', value: multiSpaceIdentityInformation.roomNumber },
{ label: 'Room Name', value: multiSpaceIdentityInformation.roomName },
{ label: 'Level', value: multiSpaceIdentityInformation.storyName },
{ label: 'Space Number', value: multiSpaceIdentityInformation.spaceNumber },
{ label: 'Space Name', value: multiSpaceIdentityInformation.spaceName }
];
Could anyone please let me know where it goes wrong with the above code?
Many thanks in advance!
Sample input
{
"Spaces": [
{
"spaceGeometry":{
"roomName": ""
"roomNumber": "",
"number": "number1"
"name": "space1"
},
"buildingStory":{
"name": "story1"
}
},
{
"spaceGeometry":{
"roomName": ""
"roomNumber": "",
"number": "number2"
"name": "space2"
},
"buildingStory":{
"name": "story2"
}
},
]
}
and desired output be like
multiSpaceIdentityInformation.roomName = "N/A"
multiSpaceIdentityInformation.roomNumber = "N/A"
multiSpaceIdentityInformation.storyName = "story1, story2"
multiSpaceIdentityInformation.spaceNumber = "number1, number2"
multiSpaceIdentityInformation.spaceName = "space1, space2"

The second parameter of reduce() is the initial value, so "N/A" will be rendered as long as spaces is an array. Here's what I would do:
const appendData = (initialValue, newValue) => {
if(!newValue) return initialValue;
if(!initialValue || initialValue === '') return newValue;
return `${initialValue}, ${newValue}`;
}
const multiSpaceIdentityInformation = spaces?.reduce(
(acc, space) => ({
roomName: appendData(acc.roomName, space.spaceIdentity.roomName),
roomNumber: appendData(acc.roomNumber, space.spaceIdentity.roomNumber),
storyName: appendData(acc.storyName, space.buildingStory?.name),
spaceNumber: appendData(acc.spaceNumber, space.spaceIdentity.number),
spaceName: appendData(acc.spaceName, space.spaceIdentity.name)
}),
{
roomName: '',
roomNumber: '',
storyName: '',
spaceNumber:'',
spaceName: ''
}
);
Object.keys(multiSpaceIdentityInformation).forEach((key) => {
if(multiSpaceIdentityInformation[key] === '')
multiSpaceIdentityInformation[key] = 'N/A';
});

Related

How to transform nested object of objects into array of objects

Hi all I have the following code
the data that I want to transform.
const obj = {
numbers: {
label: "main numbers",
pageTitle: "Numbers",
key: "1",
items: {
firstNumber: {
label: "first number",
pageTitle: "first",
key: "first"
},
secondNumber: {
label: "second number",
pageTitle: "second",
key: "second"
}
}
},
letters: {
label: "main Letters",
pageTitle: "Letters",
key: "2",
items: {
firstLetter: {
label: "first Letter",
pageTitle: "first",
key: "first"
}
}
},
signs: {
label: "main sign",
pageTitle: "Sign",
key: "3"
}
};
In my obj variable I have 3 other objects
numbers object which has items property which includes 2 other objects.
letters object which has items property which includes only one object.
signs object.
I need to transform my obj to the following way.
[
{
label:"main numbers",
pageTitle:"Numbers",
key:1,
children: [{label,pageTitle,key},{label,pageTitle,key}]
},
{
label:"main Letters",
pageTitle:"Letters",
key:1,
children: [{label,pageTitle,key}]
},
{
label:"main sign",
pageTitle:"Sign",
key:1,
children: []
},
]
for that transformation, I wrote the following code.
const transformedData = Object.values(obj).map((menuitem) => menuitem);
const data = [];
transformedData?.map((x) => {
const newData = {};
newData.label = x.label;
newData.pageTitle = x.pageTitle;
newData.key = x.key;
newData.children = x?.Object?.values(items)?.map((el) => {
newData.children.label = el.label;
newData.children.pageTitle = el.pageTitle;
newData.children.key = el.key;
});
data.push(newData);
});
Everything was working, but for children instead of printing an array it prints undefined.
Please help me to resolve this issue.
I created a function for your case.
const convert = data =>
Object.values(data)?.map(x => ({
label: x.label,
pageTitle :x.pageTitle ,
key: x.pathname,
children: x.items
? Object.values(x.items || {}).map(el => ({ label: el.label,
key:el.pathname,pageTitle:el.pageTitle }))
: null,
}));
You can use like const items = convert(obj).
xdoesn't have Objects. Change it to:
newData.children = Object.values(x.items)?.map(/*...*/);
Is this what you're after?
const transformedData = Object.values(obj).map((menuitem) => menuitem);
const data = [];
transformedData?.map((x) => {
const newData = {};
newData.label = x.label;
newData.pageTitle = x.pageTitle;
newData.key = x.key;
if(x.hasOwnProperty('items')){
newData.children = Object.values(x.items).map((el) => {
const obj={
label:el.label,
pageTitle:el.pageTitle,
key:el.key
}
return obj
})};
data.push(newData);
});
console.log(data)
Your code return undefined because inside map you didn't return anything so newData.children was never populated with anything.
Also, I think accessing and assigning newData.children.label was problematic since there was no newData.children yet. So we declare a temp obj inside map and we return it
Lastly we need to check if items is a property that exists in the first place.

How to check if a value in an array is present in other object and accordingly return a new object

I have an array
const dataCheck = ["Rohit","Ravi"];
I have another array of object
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
I want to check if any value in dataCheck is present in the userData and then return a new array with the below data
const newData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit", status: "present" },
{ name: "Ravi", status: "present" },
];
I tried to do something using loops but not getting the expected results
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" }
];
let newDataValue = {};
let newData = [];
userData.forEach((user) => {
const name = user.name;
dataCheck.forEach((userName) => {
if (name === userName) {
newDataValue = {
name: name,
status: "present"
};
} else {
newDataValue = {
name: name
};
}
newData.push(newDataValue);
});
});
console.log(newData);
My trial gives me repeated results multiple results which is just duplicates
You should use map() and a Set.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const set = new Set(dataCheck);
const output = userData.map(data => set.has(data.name) ? ({...data, status: "present"}): data)
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
A Set allows for lookups in O(1) time and therefore this algorithm works in O(n) time. If you would use the array for lookups (e.g. using indcludes(), find() etc.) the runtime would be O(n²). Although this will certainly not matter at all for such small arrays, it will become more relevant the larger the array gets.
map() is used here because you want a 1:1 mapping of inputs to outputs. The only thing to determine then is, what the output should be. It is either the input, if the value is not in the Set, or it is the input extended by one property status set to "present". You can check for the presence in a Set using the has() method and can use the ternary operator ? to make the decision which case it is.
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
// map through every object and check if name property
// exists in data check with help of filter.
// if it exists the length of filter should be 1 so
// you should return { name: el.name, status: "present" } else
// return { name: el.name }
let newData = userData.map((el) => {
if (dataCheck.filter((name) => name === el.name).length > 0) {
return { name: el.name, status: "present" };
} else {
return { name: el.name };
}
});
console.log("newdata: ", newData);
A better approach would be to use map over userData array, find for matching element in dataCheck, if found return matching element + a status key or just return the found element as it is.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const getUpdatedObject = () => {
return userData.map(userData => {
const userDetail = dataCheck.find(data => userData.name === data);
if(userDetail) return {userDetail, status:"present"}
else return {...userData}
});
}
console.log(getUpdatedObject())
Working fiddle
Loop through userData, check if name is includes in dataCheck. If true add status 'present'.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
for (let user of userData) {
if(dataCheck.includes(user.name)) {
user.status = 'present'
}
}
console.log(userData)
You are seeing repeated results due to the second loop dataCheck.forEach((userName) => { as every loop of dataCheck will fire the if/else statement and add something to the final array. However many values you add to dataCheck will be however many duplicates you get.
Only need to loop through one array and check if the value is in the other array so no duplicates get added.
const dataCheck = ["Rohit", "Ravi"];
const userData = [{ name: "Sagar" }, { name: "Vishal" }, { name: "Rohit" }, { name: "Ravi" }];
let newDataValue = {};
let newData = [];
// loop thru the users
userData.forEach((user) => {
// set the user
const name = user.name;
// check if in array
if (dataCheck.indexOf(name) >= 0) {
newDataValue = {
name: name,
status: "present",
};
}
// not in array
else {
newDataValue = {
name: name,
};
}
newData.push(newDataValue);
});
console.log(newData);
So you will do like this :
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const newUserData = userData.map( user => {
dataCheck.forEach( data => {
if( data === user.name )
user.status = "present";
});
return user;
} );
console.log( newUserData );

Create array of objects

myArray = [
{
obj: {
name: "Andy",
phone: ["11111111", "22222222"],
email: ["andy#andy.com"],
},
},
{obj: { name: "Kate", phone: ["073463653"] } },
{obj: { name: "July", email: ["july#gmail.com", "july#bes.com"] } },
];
I've tried something like this, but I can't map through the phone no and get a valid result. If you guys have an idea or some suggestions I would really appreciate it
ngOnInit() {
Object.keys(
this.myArray.map((key) => {
const objectWithData = {
[name]: key._objectReference.name,
phoneNumbers: key._objectReference.phoneNumbers.map((each) => {
phoneNumbers: each;
}),
};
return objectWithData;
})
);
}
As an alternative, I'd prefer to use reduce for something like this with the phone numbers/emails and find it more readable than the nested mapping. Also, I would change the resulting model to strings instead of arrays since you are showing a different model anyway that isn't really lists. You can modify the below to put them in arrays if you need to...
Also, you'll notice the keys are still there for undefined properties. If you want to leave the keys out you'd just have to build up the object conditionally before passing to newArr.push({});
EDIT:
After reviewing the OP I see now that you meant for names to be repeated in the results for each phone/email - and repeating the first element in the shorter array if possible (e.g., andy's one email repeating for each phone number).
The below is the best I can do for this, it works, just does not look all that elegant IMO.
The assumption is that in the case you have more phone numbers than emails or vice versa, you want to repeat the first element of the shorter array in order to always include that contact option in each contact record. Otherwise each contact record would have a phone number and email with matching indexes in the respective arrays.
I first check to determine which array is longer (phone or email) and loop only once through the longer one so you get every contact option in the loop and create contact records in the new array for each element in the longer array.
For the phone and email fields, each ugly expression determines:
-Are there any phone/email options?
-Are there options at the current index (of the longer array loop)?
-If not, is there an element in the first position to use as default?
-Else leave it undefined
function mapContacts(arr) {
var longerArr;
return arr.reduce((newArr, currEle) => {
currEle = currEle.obj;
if (currEle.phone && currEle.phone.length > (currEle.email ? currEle.email.length : 0)) {
longerArr = currEle.phone;
} else if (currEle.email && currEle.email.length > 0) {
longerArr = currEle.email;
} else {
return; // No contact info
}
longerArr.forEach((ele, index) => {
newArr.push({
name: currEle.name,
phone: currEle.phone && (currEle.phone[index] ? currEle.phone[index] :
(currEle.phone[0] && currEle.phone[0])),
email: currEle.email && (currEle.email[index] ? currEle.email[index] :
(currEle.email[0] && currEle.email[0]))
});
});
return newArr;
}, []);
}
var myArray = [
{
obj: {
name: "Andy",
phone: ["11111111", "22222222"],
email: ["andy#andy.com"]
}
},
{ obj: { name: "Kate", phone: ["073463653"] } },
{ obj: { name: "July", email: ["july#gmail.com", "july#bes.com"] } },
{ obj: { name: "Jay", phone: ["489375438"], email: ["jay#gmail.com", "jay#bes.com"] } },
];
var newArr = mapContacts(myArray);
console.log(newArr);
/* Output
[
{
"name": "Andy",
"phone": "11111111",
"email": "andy#andy.com"
},
{
"name": "Andy",
"phone": "22222222",
"email": "andy#andy.com"
},
{
"name": "Kate",
"phone": "073463653",
"email": undefined
},
{
"name": "July",
"phone": undefined,
"email": "july#gmail.com"
},
{
"name": "July",
"phone": undefined,
"email": "july#bes.com"
},
{
"name": "Jay",
"phone": "489375438",
"email": "jay#gmail.com"
},
{
"name": "Jay",
"phone": "489375438",
"email": "jay#bes.com"
}
]
*/
Does it fit an answer for you ?
first answer
const myArray =
[ { _objectReference:
{ name: "Andy"
, phoneNumbers: [ "11111111", "22222222"]
, emails: [ "andy#andy.com"]
}
}
, { _objectReference:
{ name: "Kate"
, phoneNumbers: [ "073463653"]
}
}
, { _objectReference:
{ name: "July"
, emails: [ "july#gmail.com", "july#bes.com"]
}
}
]
const newArrayContacts =
myArray.reduce((a,c)=>
{
let row = c._objectReference
, newEl = { name: row.name }
if (row.phoneNumbers) { newEl.phoneNumber = row.phoneNumbers[0] }
if (row.emails) { newEl.email = row.emails[0] }
if ( row.phoneNumbers
&& row.phoneNumbers.length > 1 )
{
row.phoneNumbers.forEach(phoneNumber =>
{
newEl.phoneNumber = phoneNumber
a.push ( {...newEl } )
})
}
else if (row.emails
&& row.emails.length > 1 )
{
row.emails.forEach(email =>
{
newEl.email = email
a.push ( {...newEl } )
})
}
else
{ a.push ( newEl ) }
return a
},[])
console.log( newArrayContacts )
second answer (Angular ?)
const myArray =
[ { name: "Andy", phoneNumbers: [ "11111111", "22222222"] , emails: [ "andy#andy.com"] }
, { name: "Kate", phoneNumbers: [ "073463653"] }
, { name: "July", emails: [ "july#gmail.com", "july#bes.com"] }
]
const newArrayContacts =
myArray.reduce((acc,row)=>
{
let newEl = { name: row.name }
if (row.phoneNumbers) { newEl.phoneNumber = [row.phoneNumbers[0]] }
if (row.emails) { newEl.email = [row.emails[0]] }
if ( row.phoneNumbers
&& row.phoneNumbers.length > 1 )
{
row.phoneNumbers.forEach((phoneNumber)=>
{
newEl.phoneNumber = [phoneNumber]
acc.push ( {...newEl } )
})
}
else if (row.emails
&& row.emails.length > 1 )
{
row.emails.forEach(email =>
{
newEl.email = [email]
acc.push ( {...newEl } )
})
}
else
{ acc.push ( newEl ) }
return acc
},[])
console.log( JSON.stringify( newArrayContacts,0,2) )
the map function returns nothing
phoneNumbers: key._objectReference.phoneNumbers.map((each) => {
phoneNumbers: each;
}),
you have to return the object
phoneNumbers: key._objectReference.phoneNumbers.map((each) => return {
phoneNumbers: each
}),
or
phoneNumbers: key._objectReference.phoneNumbers.map((each) => ({
phoneNumbers: each
})),
but that doesn't solve your overall problem.
to solve this i don't think you can use only the map function as it returns an array with the same number of elements as the original,
i would use nested for / foreach and build an array as i go

How to find a string from multiple objects?

I'm trying to update a state in my redux reducer by passing the deleted item id. The Id is located in tasks and associated column. What would a clean way to delete this item?
So far my reducer looks like this:
case DELETE_TASK:
const update = delete state.tasks[`${action.payload.id}`]
const findIdCol = ?
return {
}
const initState = {
tasks: {
"task1": {
id: "task1",
content: "hello1"
},
"task2": {
id: "task2",
content: "hello2"
},
"task3": {
id: "task2",
content: "hello3"
}
},
columns: {
"column1": {
id: "column1",
taskIds: []
},
"column2": {
id: "column2",
taskIds: []
},
"column3": {
id: "column3",
taskIds: ["task3", "task1"]
}
},
main: {
"main": {
id: "main",
taskIds: ["task2"]
}
},
columnOrder: ["column1", "column2", "column3"],
mainOrder: ["main"]
};
You can convert the columns object into its entries, allowing you to loop over each entry You can recreate the columns object, using reduce:
state.columns = Object.entries(state.columns).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== action.payload.id)
a[k] = v
return a
}, {})
Demo:
const state = {
tasks: {
task1: { id: 'task1', content: 'hello1' },
task2: { id: 'task2', content: 'hello2' },
task3: { id: 'task2', content: 'hello3' }
},
columns: {
column1: { id: 'column1',taskIds: [] },
column2: { id: 'column2',taskIds: [] },
column3: { id: 'column3',taskIds: ['task3', 'task1'] }
},
main: {
main: { id: 'main', taskIds: ['task2']}
},
columnOrder: ['column1', 'column2', 'column3'],
mainOrder: ['main']
}
const id = 'task1'
state.columns = Object.entries(state.columns).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== id)
a[k] = v
return a
}, {})
console.log(state)
You would replace id with action.payload.id in this case.
If you wanted to reuse this code, you could make it into a function:
const state = {
tasks: {
task1: { id: 'task1', content: 'hello1' },
task2: { id: 'task2', content: 'hello2' },
task3: { id: 'task2', content: 'hello3' }
},
columns: {
column1: { id: 'column1',taskIds: [] },
column2: { id: 'column2',taskIds: [] },
column3: { id: 'column3',taskIds: ['task2', 'task1'] }
},
main: {
main: { id: 'main', taskIds: ['task2']}
},
columnOrder: ['column1', 'column2', 'column3'],
mainOrder: ['main']
}
const id = 'task2'
const removeId = (o, id) => {
return Object.entries(o).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== id)
a[k] = v
return a
}, {})
}
state.columns = removeId(state.columns, id)
state.main = removeId(state.main, id)
console.log(state)
To keep your reducer function pure and state immutable, I would not recommend to update the properties of the state directly (neither with reduce() returning an object, nor with delete operator). The cleanest approach here would be to follow the pattern
grab the state
put it into variable
modify relevant parts
replace the original state with its modified copy
That would give you something, like this (e.g. if you wish to remove task3 from your default state):
//dependencies
const { createStore } = Redux
//store initialization
const defaultState = {tasks:{"task1":{id:"task1",content:"hello1"},"task2":{id:"task2",content:"hello2"},"task3":{id:"task3",content:"hello3"}},columns:{"column1":{id:"column1",taskIds:[]},"column2":{id:"column2",taskIds:[]},"column3":{id:"column3",taskIds:["task3","task1"]}},main:{"main":{id:"main",taskIds:["task2"]}},columnOrder:["column1","column2","column3"],mainOrder:["main"]},
appReducer = (state=defaultState, action) => {
switch(action.type){
case 'DELETE_TASK': {
//destructuring state into parts to be modified
const {tasks,columns,main} = state,
{payload: taskIdToDelete} = action
//iterate through tasks to delete matching id's
Object.entries(tasks).forEach(([key, {id}]) => id == taskIdToDelete && delete tasks[key])
//iterate through columns to filter out deleted task
Object.entries(columns).forEach(([key, {taskIds}]) => columns[key]['taskIds'] = taskIds.filter(taskId => taskId != taskIdToDelete))
//filter out deleted task from main
main.main.taskIds = main.main.taskIds.filter(taskId => taskId != taskIdToDelete)
return {...state, tasks, columns, main}
}
default: return state
}
},
store = createStore(appReducer)
//dispatch 'DELETE_TASK'
store.dispatch({type:'DELETE_TASK', payload: 'task3'})
//log modified state
console.log(store.getState())
.as-console-wrapper{min-height:100%}
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.4/redux.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.1/react-redux.min.js"></script><div id="root"></div>

how to split array of objects into multiple array of objects by subvalue

I need to split an Array by its objects subvalue (type).
Let's assume I have following array:
[
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
]
and I want to split the object by information.type so my final result looks like:
[
{
type:"employee",
persons:
[
{id:1,name:"John",information: { ... }},
{id:2,name:"Charles",information: { ... }
]
},
{
type:"ceo",
persons:
[
{id:3,name:"Emma",information: { ... }}
]
},
{
type:"customer",
persons:
[
{id:4,name:"Jane",information: { ... }}
]
},
]
Underscore is available at my Project. Any other helper library could be included.
Of course I could loop through the array and implement my own logic, but i was looking for cleaner solution.
This returns exactly what you want:
_.pairs(_.groupBy(originalArray, v => v.information.type)).map(p => ({type: p[0], persons: p[1]}))
A solution in plain Javascript with a temporary object for the groups.
var array = [{ id: 1, name: "John", information: { type: "employee" } }, { id: 2, name: "Charles", information: { type: "employee" } }, { id: 3, name: "Emma", information: { type: "ceo" } }, { id: 4, name: "Jane", information: { type: "customer" } }],
result = [];
array.forEach(function (a) {
var type = a.information.type;
if (!this[type]) {
this[type] = { type: type, persons: [] };
result.push(this[type]);
}
this[type].persons.push({ id: a.id, name: a.name });
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
You could use the groupBy function of underscore.js:
var empList = [
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
];
_.groupBy(empList, function(emp){ return emp.information.type; });

Categories