The approach below uses flatMap to flatten tags acquired through map. Finally, use the spread operator to assign the values from tag and the feature's name.
const result = _.flatMap(features, ({ name, tags }) =>
_.map(tags, tag => ({ name, ...tag }))
);
const features = [{
'name': 'feature1',
'tags': [{
'weight': 10,
'tagName': 't1'
}, {
'weight': 20,
'tagName': 't2'
}, {
'weight': 30,
'tagName': 't3'
}]
},
{
'name': 'feature2',
'tags': [{
'weight': 40,
'tagName': 't1'
}, {
'weight': 5,
'tagName': 't2'
}, {
'weight': 70,
'tagName': 't3'
}]
},
{
'name': 'feature3',
'tags': [{
'weight': 50,
'tagName': 't1'
}, {
'weight': 2,
'tagName': 't2'
}, {
'weight': 80,
'tagName': 't3'
}]
}
];
const result = _.flatMap(features, ({ name, tags }) =>
_.map(tags, tag => ({ name, ...tag }))
);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Here's a plain javascript solution that uses Array#reduce and Array#map with the help of Array#concat to flatten the array.
const result = features.reduce(
(result, { name, tags }) => result
.concat(tags.map(tag => ({ name, ...tag }))),
[]
);
const features = [{
'name': 'feature1',
'tags': [{
'weight': 10,
'tagName': 't1'
}, {
'weight': 20,
'tagName': 't2'
}, {
'weight': 30,
'tagName': 't3'
}]
},
{
'name': 'feature2',
'tags': [{
'weight': 40,
'tagName': 't1'
}, {
'weight': 5,
'tagName': 't2'
}, {
'weight': 70,
'tagName': 't3'
}]
},
{
'name': 'feature3',
'tags': [{
'weight': 50,
'tagName': 't1'
}, {
'weight': 2,
'tagName': 't2'
}, {
'weight': 80,
'tagName': 't3'
}]
}
];
const result = features.reduce(
(result, { name, tags }) => result
.concat(tags.map(tag => ({ name, ...tag }))),
[]
);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Answer from ryeballar on Stack OverflowOne of the easiest solutions would be, merging the nested object with parent,
_.merge(data, data.dates);
This will bring all data.dates property into data. Then delete data.dates
delete data.dates
ES6 version:
var data = {
"dates": {
"expiry_date": "30 sep 2018",
"available": "30 sep 2017",
"min_contract_period": [{
"id": 1,
"name": "1 month",
"value": false
}, {
"id": 2,
"name": "2 months",
"value": true
}, {
"id": 3,
"name": "3 months",
"value": false
}]
},
"price": {
"curreny": "RM",
"min": 1500,
"max": 2000
}
};
var {dates: {expiry_date, ...dates}, ...rest} = data;
var flatData = {dates, expiry_date, ...rest}
console.log(flatData)
One thing to note is that you have to be careful with deeply nested destructuting. In this example if data.dates is undefined and you'll try to destructure it, error will be thrown.
You can create a recursive function (getSchema) that checks if a value (val) is an object (arrays included), iterate it with _.flatMap(), and collects the keys until it hits a value which is not an object. It then joins the collected keys and returns the string.
const getSchema = (val, keys = []) =>
_.isObject(val) ? // if it's an object or array
_.flatMap(val, (v, k) => getSchema(v, [...keys, k])) // iterate it and call fn with the value and the collected keys
:
keys.join('.') // return the joined keys
const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }
const result = getSchema(invoiceObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Without lodash, the main change is to use Object.entries() to get an array of [key, value] pairs, since Array.flatMap() can't iterate objects:
const getSchema = (val, keys = []) =>
typeof val === 'object' && val !== null ? // if it's an object or array
Object.entries(val) // get [key, value] pairs of object/array
.flatMap(([k, v]) => getSchema(v, [...keys, k])) // iterate it and call fn with the value and the collected keys
:
keys.join('.') // return the joined keys
const invoiceObject = { "AllowIPNPayment": false, "AllowOnlinePayment": false, "AllowOnlineCreditCardPayment": false, "AllowOnlineACHPayment": false, "domain": "QBO", "sparse": false, "Id": "16", "SyncToken": "1", "MetaData": { "CreateTime": "2020-03-25T15:10:40-07:00", "LastUpdatedTime": "2020-03-26T11:06:49-07:00" }, "CustomField": [{ "DefinitionId": "1", "Name": "Crew #", "Type": "StringType" }], "DocNumber": "1007", "TxnDate": "2020-03-03", "CurrencyRef": { "value": "USD", "name": "United States Dollar" }, "LinkedTxn": [{ "TxnId": "32", "TxnType": "Payment" }], "Line": [{ "Id": "1", "LineNum": 1, "Description": "Custom Design", "Amount": 750, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": { "value": "4", "name": "Design" }, "UnitPrice": 75, "Qty": 10, "TaxCodeRef": { "value": "NON" } } }, { "Amount": 750, "DetailType": "SubTotalLineDetail", "SubTotalLineDetail": {} } ], "TxnTaxDetail": { "TotalTax": 0 }, "CustomerRef": { "value": "13", "name": "uiool" }, "CustomerMemo": { "value": "Thank you for your business and have a great day!" }, "SalesTermRef": { "value": "3" }, "DueDate": "2020-04-02", "TotalAmt": 750, "ApplyTaxAfterDiscount": false, "PrintStatus": "NeedToPrint", "EmailStatus": "NotSet", "BillEmail": { "Address": "uiikoool" }, "Balance": 450 }
const result = getSchema(invoiceObject)
console.log(result)
inspired by the answer given in this post and understanding you just want to get the property-names, not values, you could do it like this. sorry, this uses plain javascript.
function flattenObjectToKeyArray(ob) {
var toReturn = [];
for (var prop in ob) {
if (!ob.hasOwnProperty(prop)) continue;
if ((typeof ob[prop]) == 'object' && ob[prop] !== null) {
var flatObject = flattenObjectToKeyArray(ob[prop]);
for (var idx = 0; idx < flatObject.length; idx++) {
toReturn.push(prop + '.' + flatObject[idx]);
}
} else {
toReturn.push(prop);
}
}
return toReturn;
}
You can simply use the npm package flat.
import flat from 'flat';
const array = [
{
a: 'a',
b: {
bb: 'bb',
},
c: {
cc1: 'cc1',
cc: {
ccc: 'ccc',
ccd: 'ccd',
},
},
},
// ...
];
const flattenedArray = array.map(flat);
/*
flattenedArray === [
{
a: 'a',
b.bb: 'bb',
c.cc1: 'cc1',
c.cc.ccc: 'ccc',
c.cc.ccd: 'ccd',
},
// ...
]
*/
And if you want to implement it yourself, here's my implementation of flat:
const flat = (obj, concatenator = '.') => (
Object.keys(obj).reduce(
(acc, key) => {
if (typeof obj[key] !== 'object' || !obj[key]) {
return {
...acc,
[key]: obj[key],
};
}
const flattenedChild = flat(obj[key], concatenator);
return {
...acc,
...Object.keys(flattenedChild).reduce((childAcc, childKey) => ({ ...childAcc, [`${key}${concatenator}${childKey}`]: flattenedChild[childKey] }), {}),
};
},
{},
)
);
const flattenedArray = array.map(o => flat(o));
Those solutions will work with any depth.
You can do this without lodash (or another library) by iterating over the keys of each item in the original array, checking their type, and further iterating over their internal keys, building new keys to hold the right values.
const array = [{
id: 123,
name: 'John',
summary1: {
count: 3,
sum: 10
},
summary2: {
count: 10,
sum: 20
}
}];
let newArray = array.map(item => {
let newObj = {};
Object.keys(item).forEach(key => {
if (typeof item[key] === 'object') {
Object.keys(item[key]).forEach(innerKey => {
newObj[`${key}_${innerKey}`] = item[key][innerKey];
});
} else {
newObj[key] = item[key];
}
});
return newObj;
});
console.log(newArray);
Granted, this isn't necessarily very pretty or flexible (following your assumption of a single level deep).
This does the same as your code:
const merged = _.flatMap(superObject, ({superObjectName, subObject}) =>
_.map(subObject, ({subObjectName}) => ({
superObject: superObjectName,
subObject: subObjectName
}))
);
Each value in superObject transformed to Array with map, and then flattened inside flatMap.
You can use flatMap, get props and get the desire result like this using lodash.
var data= [{
superObjectName: "someName",
subObject: [
{subObjectName: "someSubName"},
{subObjectName: "someSubName2"}
]
}];
const result = _.flatMap(data, ({ superObjectName, subObject}) =>
_.map(subObject, ({subObjectName})=> ({superObject: superObjectName, subObject: subObjectName}))
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
You don't need to use recursion if your object isn't multiple levels deep.
Instead, you can achieve this without lodash and recursion. You could use .flatMap to get each job array, and then create an individual object for each item within that job array using .map.
See example below:
Copyconst arr = [{name: "A", job: ["1", "2"]}, {name: "B", job: ["2"]}, {name: "C", job: []}];
const spreadArrObj = arr => {
return arr.flatMap(({name, job}) => {
if(job.length < 2) return {name, job}; // if the object is empty or has one one object then return the object
return job.map(elem => ({ // map each element in the job array to its own object
name,
job: [elem]
}));
});
}
console.log(spreadArrObj(arr));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
See browser compatibility for .flatMap here.
Copylet arr = [{
name: "A",
job: ["1", "2"]
},
{
name: "B",
job: ["2"]
},
{
name: "C",
job: []
}
];
let newArr = [];
for (i = 0; i < arr.length; i++) {
var job = arr[i].job
if (job.length > 0) {
for (j = 0; j < job.length; j++) {
obj = {};
obj.name = arr[i].name;
obj.job = [job[j]];
newArr.push(obj);
}
} else {
obj = {};
obj.name = arr[i].name;
obj.job = [];
newArr.push(obj);
}
}
console.log(newArr);
Run code snippetEdit code snippet Hide Results Copy to answer Expand