I am modifying an array of object using pure function but facing some challenge to modify userDetails object.
My requirement is instead 1, I need to show true inside permissions object. Please check my code and suggest. I know, I am doing some small mistake but still trying to identify.
I dont want main data source should get affected. Please suggest if you have any other good option
let data = [
{ id: 1,
title: 'admin1',
permissions: {
userDetails: { activeUser: 1, team: null},
salaryList: { abc: 1, def: 2, asdf: 0, poi: 1}
}
},
{ id: 1,
title: 'admin1',
permissions: {
userDetails: { activeUser: 1, team: null},
salaryList: { abc: 0, def: 1, asdf: null, poi: 0,wew: 1, aaa: 1}
}
},
]
let modifiedObjs = data.map( record => {
return {
id: record.id,
title: record.title + " Edited Object",
permissions: handlePermission(record.permissions)
}
})
function handlePermission(permissions){
Object.keys(permissions).forEach((key)=> {
Object.keys(permissions[key]).forEach((obj) => {
if(permissions[key][obj]===null){
delete permissions[key][obj]
}else{
const value = permissions[key][obj];
const finalV = value === 0? false : value ===1? true : value === 2 ? null : value;
permissions[key][obj] = finalV
}
})
})
return permissions
}
console.log(data, "main data")
console.log(modifiedObjs, "modified data")
The main issue with your code is that permissions is a reference inside of handlePermission, meaning that when you delete from it, it will also the key will also be removed from your original object (as they're sharing the same reference). One way to fix this is to instead build a new object. Whenever you find a key you want to keep, you can add it to the new object. Then, instead of deleting, you can simply not add the key/value to the object.
See example below:
const data = [{ id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 1, def: 2, asdf: 0, poi: 1 } } }, { id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 0, def: 1, asdf: null, poi: 0, wew: 1, aaa: 1 } } }, ];
let modifiedObjs = data.map( record => {
return {
id: record.id,
title: record.title + " Edited Object",
permissions: handlePermission(record.permissions)
}
});
function handlePermission(permissions){
const newPermissions = {};
Object.keys(permissions).forEach((key)=> {
newPermissions[key] = {}; // set object for key
Object.keys(permissions[key]).forEach((obj) => {
if(permissions[key][obj]!==null){
const value = permissions[key][obj];
const finalV = value === 0? false : value ===1? true : value === 2 ? null : value;
newPermissions[key][obj] = finalV;
}
});
});
return newPermissions;
}
console.log(data, "main data")
console.log(modifiedObjs, "modified data")
I would instead approach it like so, which uses Object.entries() to get a [key, value] pair array of entries from your object, along with Object.fromEntries() to reconstruct the object after its values have been mapped/filtered:
const data = [{ id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 1, def: 2, asdf: 0, poi: 1 } } }, { id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 0, def: 1, asdf: null, poi: 0, wew: 1, aaa: 1 } } }, ];
const modifiedObjs = data.map(({id, title, permissions}) => ({
id,
title: title + " Edited Object",
permissions: Object.fromEntries(Object.entries(permissions).map(([key, obj]) => [
key,
Object.fromEntries(Object.entries(obj).filter(([,v]) => v !== null)
.map(([k, v]) => [k, v > 1 ? null : !!v]))
]))
}));
console.log(data, "main data");
console.log(modifiedObjs, "modified data");
Related
I have a question about how I can delete the existing elements, for example, in my case "Tallas" is repeated, could you please help me? Thank you very much to those who are willing to help me to solve this problem
const data =
[ { atributos: { Tallas: [{ id: 0, name: 'XS' }, { id: 1, name: 'S' }] }}
, { atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
The idea is to have this json format with the last "Tallas" since it is the last one that I added through my dynamic form.
const expected =
[{ atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
How do I do this is there a way to do it, I've tried with filter plus the findindex but I can't get to eliminate the repetition of the json res= new.filter((arr, index, self) => index === self.findIndex( (t) => (t.attributes === arr.attributes )))
To unique the array of objects, we can use the Javascript Set module, if the array has complex nested objects, we can stringify each object before creating new Set data. this below function will unique the array of complex objects.
function unique_array(array = []) {
const newSetData = new Set(array.map((e) => JSON.stringify(e)));
return Array.from(newSetData).map((e) => JSON.parse(e));
}
this is a function that takes an array and return the same array but delete every duplicated item
function removeDuplicates(arr) {
return arr.filter((item,
index) => arr.indexOf(item) === index);
}
I didn't understant the part written in spanish so I hope this is what you are looking for
This is a solution specific to your question. this is not a generic solution.
const data = [
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
{
atributos: {
Calzado: [
{ id: 0, name: "10" },
{ id: 1, name: "9.5" },
],
},
},
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
];
function uniqueArray(array) {
const resultObject = array.reduce((acc, eachValue) => {
let keys = Object.keys(eachValue.atributos);
keys.forEach((eachKey) => {
if (!acc[eachKey]) {
acc[eachKey] = [];
}
let list = eachValue["atributos"][eachKey].map(
(each) => each.id + "-" + each.name
);
acc[eachKey].push(...list);
});
return acc;
}, {});
const resultArray = Object.keys(resultObject).reduce((acc, each) => {
let setData = Array.from(new Set(resultObject[each]));
acc.push({
atributos: {
[each]: setData.map((e) => {
return { id: e.split("-")[0], name: e.split("-")[1] };
}),
},
});
return acc;
}, []);
return resultArray;
}
const result = uniqueArray(data)
console.log("result ", JSON.stringify(result, null, 2));
I have an array like the picture, now i want to check if the id is duplicate then combine it and the value of "key" will become key with the value is 1 or 0
final result :
[
{
id: 4,
view: 1,
add: 1,
edit: 1,
delete: 0,
},
{
id: 7,
view: 1,
add: 1,
edit: 0,
delete: 0,
},
];
code
const App = () => {
const arr = [
{ id: 4, key: "view" },
{ id: 4, key: "add" },
{ id: 7, key: "view" },
{ id: 7, key: "add" },
{ id: 4, key: "edit" }
];
arr.map((item,index) => {
// check the next id is exists
if(arr[index + 1] && item.id === arr[index + 1].id) {
//
}
})
return (
<div></div>
);
};
create a object template which initialises all the values to zero.
reduce over the array of objects. If the id is not found on the initial object as a key add it, and set its value to an object containing the id, and a merged copy of template.
Set any found keys to 1.
Grab the resulting object's values to get an array of objects matching your expected output.
const arr = [
{ id: 4, key: "view" },
{ id: 4, key: "add" },
{ id: 7, key: "view" },
{ id: 7, key: "add" },
{ id: 4, key: "edit" }
];
// Create an object template
const tmpl = { view: 0, add: 0, edit: 0, delete: 0 };
// `reduce` over the array of objects. If the id
// doesn't exist on the initialised object as a key
// add it and set it's value to an object containing
// the id, and a copy of the object template, and
// then update any keys that are found.
const obj = arr.reduce((acc, c) => {
const { id, key } = c;
acc[id] ??= { id, ...tmpl };
acc[id][key] = 1;
return acc;
}, {});
console.log(Object.values(obj));
Additional documentation
Logical nullish assignment
Spread syntax
Destructuring assignment
const data = [
{ id: 4, key: "view" },
{ id: 4, key: "add" },
{ id: 7, key: "view" },
{ id: 7, key: "add" },
{ id: 4, key: "edit" }
]
const result = data.reduce((p, c) => {
const found = p.findIndex(p => p.id === c.id);
if (found !== -1) {
p[found][c.key] = 1;
} else {
const tmpObj = {
id: c.id,
view: 0,
add: 0,
edit: 0,
delete: 0
}
tmpObj[c.key] = 1;
p.push(tmpObj)
}
return p;
}, []);
console.log(result);
const arr = [
{ id: 4, key: "view" },
{ id: 4, key: "add" },
{ id: 7, key: "view" },
{ id: 7, key: "add" },
{ id: 4, key: "edit" }
];
const r = arr.reduce((c, d, i) => {
const {
id,
key
} = d;
if (c[id]) {
c[id][key] = 1
} else {
c[id] = {
id,
view: 0,
edit: 0,
add: 0,
delete: 0
}
c[id][key] = 1
}
return c
}, {});
const result = Object.values(r);
console.info(result)
I have an SQLite database table
+---------------------------------------------------+
| id | Cat_Name | Parent_ID |
|---------------------------------------------------+
| 1 | Asset | NULL |
+---------------------------------------------------+
| 2 | Bank | 1 |
+---------------------------------------------------+
| 3 | Cash | 1 |
+---------------------------------------------------+
| 4 | Petty Cash | 3 |
+---------------------------------------------------+
| 5 | ABC Bank | 2 |
+---------------------------------------------------+
| 6 | Dollar Account | 2 |
+---------------------------------------------------+
i can fetch the data as below
[{ id: 1, Category_Name: "Asset", Parent_ID: 0},
{ id: 2, Category_Name: "Bank", Parent_ID: 1},
{ id: 3, Category_Name: "Cash", Parent_ID: 1},
{ id: 4, Category_Name: "Petty_Cash", Parent_ID: 3},
{ id: 5, Category_Name: "ABC_Bank", Parent_ID: 2},
{ id: 6, Category_Name: "Dollar_Account", Parent_ID: 2}]
In this table, category and subcategory created by the user, we can't assume how many parent and child categories will be in the table
Now I want pass the data as a nested javascript object to the front end
example
{Asset: {Bank: {ABC Bank: 5}, {Dollar Account: 6}
},
{Cash:{PettyCash: 4}, if any...}
}
Could anybody can help to get this result in the best way...
Thanks in advance
I suggest you change the design of the output object. I think the array approach would be better for the frontend.
const rawData = [
{ id: 1, Category_Name: "Asset", Parent_ID: 0},
{ id: 2, Category_Name: "Bank", Parent_ID: 1},
{ id: 3, Category_Name: "Cash", Parent_ID: 1},
{ id: 4, Category_Name: "Petty Cash", Parent_ID: 3},
{ id: 5, Category_Name: "ABC Bank", Parent_ID: 2},
{ id: 6, Category_Name: "Dollar Account", Parent_ID: 2},
{ id: 7, Category_Name: "Another Wallet", Parent_ID: 4},
];
const getParentDeep = (arr, targetId) => arr.find(({ id }) => id === targetId)
?? arr.flatMap(({ children }) => getParentDeep(children, targetId))
.filter(e => e)
.at(0);
const result = rawData
.sort(({ Parent_ID: a }, { Parent_ID: b }) => a - b)
.reduce((acc, { id, Category_Name, Parent_ID }) => {
const obj = { id, name: Category_Name, children: [] };
const parentObj = getParentDeep(acc, Parent_ID);
if (parentObj) parentObj.children.push(obj)
else acc.push(obj);
return acc;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }
The result will look like this:
[{
id: 1,
name: "Asset",
children: [{
id: 2,
name: "Bank",
children: [{
id: 5,
name: "ABC Bank",
children: []
}, {
id: 6,
name: "Dollar Account",
children: []
}]
}, {
id: 3,
name: "Cash",
children: [{
id: 4,
name: "Petty Cash",
children: [{
id: 7,
name: "Another Wallet",
children: []
}]
}]
}]
}]
Presented below is one possible way to achieve the desired objective. Admittedly, it is not very elegant (& possibly not the most-efficient).
Code Snippet
// helper method to recursively-add to object
const recurAdd = (arr, idx, res) => {
// when "idx" exceeds length of array "arr",
// simply return existing result "res" object
if (idx >= arr.length) return res;
// de-structure to access parent-id & id for current elt
const { Parent_ID, id } = arr[idx];
if (Parent_ID in res) {
// parent-id exists at current object,
// so, add "id" to same object (mutate)
res[Parent_ID][id] = {};
// make recursive call for "next" elt in "arr"
return recurAdd(arr, idx+1, res);
} else {
// find next-level object where current elt will fit
const foundIt = Object.values(res).map(obj => recurAdd(arr, idx, obj));
// NOTE: "obj" is part of "res" and it gets mutated
// if found, make recursive call
if (foundIt.some(x => x !== false)) return recurAdd(arr, idx+1, res);
};
// in case parent-id is not found, simply return false
return false;
};
// helper method to substitute "id" with "category names"
const recurNamify = (obj, myMap) => (
// reconstruct object from key-value pairs of intermediate result
Object.fromEntries(
// generate intermediate result of key-value pairs
Object.entries(obj)
.map(([k, v]) => (
// substitute key (ie, "id") with category-name
Object.keys(v).length === 0
? [myMap[k], k]
: [myMap[k], recurNamify(v, myMap)]
))
// when "v" is not an empty object, make recursive call
)
);
// transform the array into nested object
const myTransform = arr => {
// first transform "Number" to "string" for id and parent-id
// because JS-object keys are string type
const myArr = arr.map(ob => ({
...ob,
id: ob.id.toString(),
Parent_ID: ob.Parent_ID.toString()
}));
// generate a dictionary/map for "id" to category-name
const myMap = myArr.reduce(
(acc, itm) => {
acc[itm.id] = itm.Category_Name
return acc;
},
{}
);
// find the index of root (ie, parent id is zero)
const rIdx = myArr.findIndex(({ Parent_ID }) => Parent_ID === '0');
// obtain the root & mutate "arr" by removing the root
const [root] = myArr.splice(rIdx, 1);
// use the helper methods to transform
return recurNamify(recurAdd(myArr, 0, {[root.id]: {}}), myMap);
};
const rawData = [
{ id: 1, Category_Name: "Asset", Parent_ID: 0},
{ id: 2, Category_Name: "Bank", Parent_ID: 1},
{ id: 3, Category_Name: "Cash", Parent_ID: 1},
{ id: 4, Category_Name: "Petty_Cash", Parent_ID: 3},
{ id: 5, Category_Name: "ABC_Bank", Parent_ID: 2},
{ id: 6, Category_Name: "Dollar_Account", Parent_ID: 2}
];
console.log('transformed: ', myTransform(rawData));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
PS: If you'd like to add value to stackoverflow community,
Please consider reading: What to do when my question is answered
Thank you !
Here's another linked list variation, but with bi-directional object references and JSON de-/serialization in acknowledgement of the client/server relationship:
The Stack Overflow code snippet virtual console doesn't show interactive object relationships like your browser's JS console, so copy and paste this into your JS console to see the relational references in the final linked list value.
/** Conceptually similar to CSV when stringified, but preserves JSON types */
function compact (keysOrMappedKeys, array) {
const inputKeys = [];
let outputKeys = [];
const keysAreMapped = Array.isArray(keysOrMappedKeys[0]);
if (keysAreMapped) {
for (const [keyIn, keyOut] of keysOrMappedKeys) {
inputKeys.push(keyIn);
outputKeys.push(keyOut);
}
}
else {
for (const key of keysOrMappedKeys) inputKeys.push(key);
outputKeys = inputKeys;
}
const rows = [];
for (const obj of array) {
const row = [];
for (const key of inputKeys) row.push(obj[key]);
rows.push(row);
}
return [outputKeys, rows];
}
// Not actually needed for this answer:
/** The reverse of the `compact` function */
function expand ([keys, rows]) {
return rows.map(array => {
const obj = {};
for (const [index, key] of keys.entries()) obj[key] = array[index];
return obj;
});
}
/** Expects keys in the order `[ownId, parentId, ...others]` */
function createLinkedObjectList ([keys, rows]) {
const map = new Map(rows.map(row => {
const obj = {};
const iter = keys.entries();
const [ownIdIndex] = iter.next().value;
const ownId = row[ownIdIndex];
const [parentIdIndex] = iter.next().value;
const parentId = row[parentIdIndex];
for (const [index, key] of iter) obj[key] = row[index];
return [ownId, {id: ownId, parentId, value: obj}];
}));
for (const obj of map.values()) {
const parent = map.get(obj.parentId);
if (typeof parent !== 'undefined') {
obj.parent = parent;
(parent.children ??= []).push(obj);
}
delete obj.parentId;
}
return [...map.values()];
}
// Use: On the server:
// From the SQLite db:
const input = [
{ id: 1, Category_Name: "Asset", Parent_ID: 0},
{ id: 2, Category_Name: "Bank", Parent_ID: 1},
{ id: 3, Category_Name: "Cash", Parent_ID: 1},
{ id: 4, Category_Name: "Petty_Cash", Parent_ID: 3},
{ id: 5, Category_Name: "ABC_Bank", Parent_ID: 2},
{ id: 6, Category_Name: "Dollar_Account", Parent_ID: 2},
];
// Optionally, rename the keys when compacting the data structure:
const mappedKeys = [
['id', 'id'], // The ID key needs to be first
['Parent_ID', 'parent'], // The parent ID key needs to be second
// The order of the remaining keys is simply preference:
['Category_Name', 'name'],
];
const compacted = compact(mappedKeys, input);
/*
Or, just use the original key names:
const keys = [
'id', // The ID key needs to be first
'Category_Name', // The parent ID key needs to be second
// The order of the remaining keys is simply preference:
'Parent_ID',
];
const compacted = compact(keys, input);
*/
// You can send this JSON string to the client
const json = JSON.stringify(compacted);
console.log(json); // [["id","parent","name"],[[1,0,"Asset"],[2,1,"Bank"],[3,1,"Cash"],[4,3,"Petty_Cash"],[5,2,"ABC_Bank"],[6,2,"Dollar_Account"]]]
// Use: On the client:
/* After receiving the json from the server:
const json = await getDataFromServer();
Expand it into a linked list with bi-directional references
between actual parent and children objects.
This is where the order of the keys matters: */
const list = createLinkedObjectList(compacted);
console.log(list); /* Looks like this:
[
{
id: 1,
value: { name: 'Asset' },
children: [
{ id: 2, ... },
{ id: 3, ... },
],
},
{
id: 2,
value: { name: 'Bank' },
parent: { id: 1, ... },
children: [
{ id: 5, ... },
{ id: 6, ... },
],
},
{
id: 3,
value: { name: 'Cash' },
parent: { id: 1, ... },
children: [
{ id: 4, ... },
],
},
{
id: 4,
value: { name: 'Petty_Cash' },
parent: { id: 3, ... },
},
{
id: 5,
value: { name: 'ABC_Bank' },
parent: { id: 2, ... },
},
{
id: 6,
value: { name: 'Dollar_Account' },
parent: { id: 2, ... },
},
]
*/
This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I am getting data like below :
data.filter = {
selectedFilterItems: [
{
name: "0-435",
categoryId: "0_435",
code: "price",
totalItems: 1,
},
{
name: "MULTI",
categoryId: "7608",
count: 633,
code: "color",
subcategories: [],
totalItems: 1,
},
{
name: "Pastel Tie Dye",
categoryId: "566962",
count: 1,
code: "color",
subcategories: [],
totalItems: 1,
},
],
totalItems: 3,
};
I need to create a json like below, where type will be the "code" i get from above data (i.e data.filter)
& the value will be comma seperated for common code.
Desired Output :
filters: [
{
type: 'price',
value: '0-435'
}
]
exg for color there will be only one type = color & its value will be "MULTI,Pastel Tie Dye".
if only one distinct code is there in data.filter then only that data will be in filters.
Here is the code which i am trying
if(data.filter) {
const selectedFilterItems = data.filter.selectedFilterItems.reduce((property, attribute) => {
if (property[attribute.code]) {
property[attribute.code].value += `,${attribute.name}`;
} else {
property[attribute.code] = { type: attribute.code, value: attribute.name };
}
return property;
}, {});
filter_data = Object.values(selectedFilterItems);
}
i need to add to the "filter_data" variable.
{
type: 'category',
value: '7608' // where the value will be there "selectedFilterItems's any of the property's categoryId's value.
}
You can use reduce() to filter the data and extract it with Object.values()
const data = { selectedFilterItems: [ { name: "0-435", categoryId: "0_435", code: "price", totalItems: 1, }, { name: "MULTI", categoryId: "7608", count: 633, code: "color", subcategories: [], totalItems: 1, }, { name: "Pastel Tie Dye", categoryId: "566962", count: 1, code: "color", subcategories: [], totalItems: 1, }, ], totalItems: 3, };
const groupByData = data.selectedFilterItems.reduce((acc, b) => {
if (acc[b.code]) {
acc[b.code].value += `,${b.name}`;
} else {
acc[b.code] = { type: b.code, value: b.name };
}
return acc;
}, {});
const output = Object.values(groupByData);
console.log(output);
let data = {};
data.filter = {
selectedFilterItems: [
{
name: "0-435",
categoryId: "0_435",
code: "price",
totalItems: 1,
},
{
name: "MULTI",
categoryId: "7608",
count: 633,
code: "color",
subcategories: [],
totalItems: 1,
},
{
name: "Pastel Tie Dye",
categoryId: "566962",
count: 1,
code: "color",
subcategories: [],
totalItems: 1,
},
],
totalItems: 3,
};
let filters = [];
let codes = []
data.filter.selectedFilterItems.forEach((elem)=>codes.push(elem.code)); //store all codes in an array
codes = [...new Set(codes)] //create a set of codes which will only contain unique entities
codes.forEach(code=>{ //Iterate over the codes
let obj = {}, val="";
data.filter.selectedFilterItems.forEach(elem=>{ //Iterate over each selectedFilterItems object
if(elem.code === code){
val += elem.name + ","; // Add the name to val if the code matches
}
})
val = val.substring(0, val.length-1); //Remove last char that will be comma
obj["type"] = code;
obj["value"] = val;
filters.push(obj)
})
console.log(filters)
Given an array like this:
[
{ id: 1, emailAddresses: ["bill#test.com", "bob#test.com"] },
{ id: 2, emailAddresses: ["sarah#test.com" },
{ id: 3, emailAddresses: ["jane#test.com", "laura#test.com", "paul#test.com"]
]
How could I use Javascript to reduce this to an array like this:
[
{ id: 1, emailAddress: "bill#test.com" },
{ id: 1, emailAddress: "bob#test.com" },
{ id: 2, emailAddress: "sarah#test.com" },
{ id: 3, emailAddress: "jane#test.com" },
{ id: 3, emailAddress: "laura#test.com" },
{ id: 3, emailAddress: "paul#test.com" }
]
I've read about the functions reduce, flat, map and so on and read lots of the questions on SO about using them but I can't find anything that's asking quite the same as this and I can't get my head around using those functions to do it.
You could use flatMap
const input = [
{ id: 1, emailAddresses: ["bill#test.com", "bob#test.com"] },
{ id: 2, emailAddresses: ["sarah#test.com"] },
{ id: 3, emailAddresses: ["jane#test.com", "laura#test.com", "paul#test.com"] }
]
const output = input.flatMap(o =>
o.emailAddresses.map(e => ({ id: o.id, emailAddress: e }) )
)
console.log(output)
If flatMap is not supported, you could use a nested for...of loop:
const input = [{id:1,emailAddresses:["bill#test.com","bob#test.com"]},{id:2,emailAddresses:["sarah#test.com"]},{id:3,emailAddresses:["jane#test.com","laura#test.com","paul#test.com"]}];
const output = []
for (const { id, emailAddresses } of input)
for (const emailAddress of emailAddresses)
output.push({ id, emailAddress })
console.log(output)
You can map over your data and then use reduce to flatten the resulting array:
const result = data
.map(datum => {
return datum.emailAddresses.map(emailAddress => {
return { id: datum.id, emailAddress };
});
})
.reduce((result, current) => {
return [...result, ...current];
}, []);
We can use Array.prototype.reduce to go over each object in the array and take into consideration the multiple values in the emailAddress property array and create separate object for each one and finally accumulate the new objects in the new array (r):
const data = [
{ id: 1, emailAddresses: ["bill#test.com", "bob#test.com"] },
{ id: 2, emailAddresses: ["sarah#test.com"] },
{ id: 3, emailAddresses: ["jane#test.com", "laura#test.com", "paul#test.com"]}
]
const flat = data.reduce((r, e) => {
e.emailAddresses.forEach((obj) => r.push({id: e.id, emailAddresses : obj }));
return r;
}, []);
console.log(flat);
You can use reduce and map
const data = [
{ id: 1, emailAddresses: ["bill#test.com", "bob#test.com"] },
{ id: 2, emailAddresses: ["sarah#test.com"] },
{ id: 3, emailAddresses: ["jane#test.com", "laura#test.com", "paul#test.com"]}
]
const flat = (toFlatten) =>
toFlatten.reduce((r,c)=> {
r.push(...c.emailAddresses.map(email=>({id: c.id, emailAddress: email})))
return r
}, [])
console.log(flat(data))
Here is a solution that doesn't use any array prototype but does, instead, take advantage of function generators.
The script below iterates the array, acquire all keys of the element except emailAddresses, which is handled separately, and for each email address it yields an object filled with the single email address and the rest of the data.
This solution iterate the original array only once.
Because it uses function generators, this solution is widely supported, it just won't work on IE due it's lack of support for function generators, despite babel or TSC can easily add compatibility to that.
const input = [
{ id: 1, emailAddresses: ["bill#test.com", "bob#test.com"] },
{ id: 2, emailAddresses: ["sarah#test.com"] },
{ id: 3, emailAddresses: ["jane#test.com", "laura#test.com", "paul#test.com"] }
];
function* flattenEmailAddresses(arr) {
for (var {emailAddresses, ...keys} of arr) {
for (var emailAddress of emailAddresses) yield {...keys, emailAddress};
}
}
console.log([...flattenEmailAddresses(input)]);