Change objects structure - javascript

I have an array of objects:
const res = [
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 311,
ARTICLE_NAME: "Product one",
WEEK: 202027,
FORECAST: 81928.37,
PROMO: 0,
SALES: 92848,
SAFETY_STOCK: 57704.79,
STORES_COUNT: 132,
ADD_FCST: null
},
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 311,
ARTICLE_NAME: "Product one",
WEEK: 202028,
FORECAST: 84278.85,
PROMO: 0,
SALES: null,
SAFETY_STOCK: 64000.39,
STORES_COUNT: 144,
ADD_FCST: null
},
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 316,
ARTICLE_NAME: "Product two",
WEEK: 202027,
FORECAST: 89112.97,
PROMO: 0,
SALES: 98007,
SAFETY_STOCK: 59509.31,
STORES_COUNT: 142,
ADD_FCST: null
},
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 316,
ARTICLE_NAME: "Product two",
WEEK: 202028,
FORECAST: 85129.4,
PROMO: 0,
SALES: null,
SAFETY_STOCK: 63409.61,
STORES_COUNT: 144,
ADD_FCST: null
}
];
I want to group some fields in WEEKS, e.g:
const expected = [
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 311,
ARTICLE_NAME: "Product one",
FORECAST: [{ 202026: 97555.64 }, { 202027: 98336.45 }],
PROMO: [{ 202026: 0 }, { 202027: 0 }],
SALES: [{ 202026: 95911.77 }, { 202027: null }],
SAFETY_STOCK: [{ 202026: 63622.28 }, { 202027: 72852.62 }],
STORES_COUNT: [{ 202026: 135 }, { 202027: 143 }],
ADD_FCST: [{ 202026: null }, { 202027: null }]
},
{
KIND_ID: 2229,
NAME: "Group",
SKU_ID: 316,
ARTICLE_NAME: "Product two",
FORECAST: [{ 202026: 104125.69 }, { 202027: 101147.07 }],
PROMO: [{ 202026: 0 }, { 202027: 0 }],
SALES: [{ 202026: 102281 }, { 202027: null }],
SAFETY_STOCK: [{ 202026: 53709.55 }, { 202027: 60675.26 }],
STORES_COUNT: [{ 202026: 143 }, { 202027: 144 }],
ADD_FCST: [{ 202026: null }, { 202027: null }]
}
];
If it helps i have array of weeks and values need to be grouped:
const weeks = [202027, 202028];
const keys = ['FORECAST','PROMO','SALES','SAFETY_STOCK','STORES_COUNT','ADD_FCST'];
I tried to solve with reduce and underscore method groupBy but this not helped. How to group this res array to expected?
Dummy text, because: It looks like your post is mostly code; please add some more details. But that's all what i want to tell

I tried to solve with reduce
That's what I'd use, although there is a few steps involved as this isn't a classic grouping schematic.
Below is an example.
const res=[{KIND_ID:2229,NAME:"Group",SKU_ID:311,ARTICLE_NAME:"Product one",WEEK:202027,FORECAST:81928.37,PROMO:0,SALES:92848,SAFETY_STOCK:57704.79,STORES_COUNT:132,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:311,ARTICLE_NAME:"Product one",WEEK:202028,FORECAST:84278.85,PROMO:0,SALES:null,SAFETY_STOCK:64000.39,STORES_COUNT:144,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:316,ARTICLE_NAME:"Product two",WEEK:202027,FORECAST:89112.97,PROMO:0,SALES:98007,SAFETY_STOCK:59509.31,STORES_COUNT:142,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:316,ARTICLE_NAME:"Product two",WEEK:202028,FORECAST:85129.4,PROMO:0,SALES:null,SAFETY_STOCK:63409.61,STORES_COUNT:144,ADD_FCST:null}];
const grpFields = ['FORECAST', 'PROMO', 'SALES', 'SAFETY_STOCK',
'STORES_COUNT', 'ADD_FCST'];
const o = res.reduce((a, v) => {
const {KIND_ID, NAME, SKU_ID, ARTICLE_NAME} = v;
let f = a.find(f => f.SKU_ID === v.SKU_ID);
if (!f) {
f = {KIND_ID, NAME, SKU_ID, ARTICLE_NAME};
grpFields.forEach(k => f[k] = []);
a.push(f);
}
grpFields.forEach(k => f[k].push({[v.WEEK]: v[k]}));
return a;
},[]);
console.log(o);
To make this a generic function, you will need to pass a few parameters.
Below is an example.
arr = The array your wanting to group
grpFields = The fields that you want the grouping applied to
grpValue = The field value your grouping
keyField = What field we are grouping on
stdFields = Other fields you want as part of the result
const res=[{KIND_ID:2229,NAME:"Group",SKU_ID:311,ARTICLE_NAME:"Product one",WEEK:202027,FORECAST:81928.37,PROMO:0,SALES:92848,SAFETY_STOCK:57704.79,STORES_COUNT:132,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:311,ARTICLE_NAME:"Product one",WEEK:202028,FORECAST:84278.85,PROMO:0,SALES:null,SAFETY_STOCK:64000.39,STORES_COUNT:144,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:316,ARTICLE_NAME:"Product two",WEEK:202027,FORECAST:89112.97,PROMO:0,SALES:98007,SAFETY_STOCK:59509.31,STORES_COUNT:142,ADD_FCST:null},{KIND_ID:2229,NAME:"Group",SKU_ID:316,ARTICLE_NAME:"Product two",WEEK:202028,FORECAST:85129.4,PROMO:0,SALES:null,SAFETY_STOCK:63409.61,STORES_COUNT:144,ADD_FCST:null}];
function groupData(arr, grpFields, grpValue, keyField, stdFields) {
return arr.reduce((a, v) => {
let f = a.find(f => f[keyField] === v[keyField]);
if (!f) {
f = Object.fromEntries(stdFields.map(k => [k, v[k]]));
grpFields.forEach(k => f[k] = []);
a.push(f);
}
grpFields.forEach(k => f[k].push({[v[grpValue]]: v[k]}));
return a;
},[]);
}
console.log(
groupData(
res,
['FORECAST', 'PROMO', 'SALES', 'SAFETY_STOCK','STORES_COUNT', 'ADD_FCST'],
'WEEK',
'SKU_ID',
['KIND_ID', 'NAME', 'SKU_ID', 'ARTICLE_NAME']
)
);

Related

How to filter nested arrays by searching

I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
]};
What I'm trying to achieve is if I search for 'nodata' for example my result should be
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
}
]
}
];
Another option if I search for 'spa' my result should be
let array = [
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
}
]
}
];
I have been super confused and I decided to get some help. Thank you for your helps guys!
The following function should do the trick for you:
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
It recursively goes through the data array, looks for any entries that have the specified search term, and if so, places it into the newly constructed object. It will preserve the nested shape of the object, which may or may not be what is needed. Feel free to tweak the algorithm to your own needs.
let allData = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
];
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
console.log('----- Search: nodata')
console.log(JSON.stringify(searchData(allData, 'nodata'), null, 2))
console.log('----- Search: spa')
console.log(JSON.stringify(searchData(allData, 'spa'), null, 2))

Extracting objects out of arrays

I want to map from this:
companies: {
apples: {
Q7: {
price: 1,
},
Q6: {
price: 1,
},
peaches: {
Q7: {
price: 1,
},
Q6: {
price: 1,
},
},
};
to this:
{ "companies": {
"apples": [
{
"name": "Q7",
"price": 1
},{
"name": "Q6",
"price": 1
}
],
"peaches": [
{
"name": "Q7",
"price": 1
},{
"name": "Q6",
"price": 1
}
]
}
}
How I am trying to achieve this:
I have a selector which gives me the companies object and then I map over it and assemble my object but I don't get it quite right.
This is my function:
const weaponProducts = Object.entries(getCompanies(state)).map(([companyType, companies]) => {
const prod = Object.entries(companies).map(([key, companies]) => {
return {
name: key,
price: companies.price
}
});
return {
[companyType]: prod
};
});
getCompanies(state) returns the following object:
{
"companies": {
"apples": {
"Q7": {
"price": 1
},
"Q6": {
"price": 1
}
},
"peaches": {
"Q7": {
"price": 1
},
"Q6": {
"price": 1
}
}
}
}
The result of the function is the following. But as explained I want it to look like the second code section of my post.
[
{
"apples": [
{
"name": "Q7",
"price": 1
},
{
"name": "Q6",
"price": 1
},
]
},
{
"peaches": [
{
"name": "Q7",
"price": 1
},
{
"name": "Q6",
"price": 1
},
]
}
]
since your desired output is an object, not an array, you should use reduce instead of map:
let companies = {
apples: {
Q7: {
price: 1,
},
Q6: {
price: 1,
},
},
peaches: {
Q7: {
price: 1,
},
Q6: {
price: 1,
},
},
}
let fruit = Object.keys(companies)
let output = fruit.reduce((output, currentFruit) => {
output[currentFruit] = Object.keys(companies[currentFruit]).map(q => {
return { name: q, price: companies[currentFruit][q].price }
})
return output
}, {});
console.log(output);
(I think there was a syntax error in your companies object, I corrected in the snippet)
You can also take entries and then map the objects accordingly.
var companies = { apples: { Q7: { price: 1, }, Q6: { price: 1, }, }, peaches: { Q7: { price: 1, }, Q6: { price: 1, } }};
const result = (inputObj) =>
Object.fromEntries(
Object.entries(inputObj).map(([key, obj]) => [
key,
Object.entries(obj).map(([name, val]) => ({ name, ...val })),
])
);
console.log(result(companies));
If you're down to try something new, here's a different way to express the desired transformation using a library I authored.
const x = { companies: { apples: { Q7: { price: 1, }, Q6: { price: 1, }, }, peaches: { Q7: { price: 1, }, Q6: { price: 1, }, }, } }
const { pipe, fork, map, tap, get } = rubico
const y = map(map(pipe([
Object.entries,
map(fork({
name: get(0),
price: get([1, 'price']),
})),
])))(x)
console.log(JSON.stringify(y, null, 2))
<script src="https://unpkg.com/rubico/index.js"></script>

Check if an element is common between 2 arrays and then assign values from it

In my angular 8 application, I have 2 arrays:
array1 = [{
"SubType": "2|3|4|5|6",
},
{
"SubType": "2",
},
{
"SubType": "3|4",
},
{
"SubType": "6",
},
{
"SubType": "3|6",
},
]
&
array2 = [{
"id": 2,
"type": "1",
},
{
"id": 3,
"type": "5",
},
{
"id": 4,
"type": "4",
},
{
"id": 5,
"type": "3",
},
{
"id": 6,
"type": "2",
}
]
I am trying to check each "SubType" in array1 and see if that element(id) is present in array2 and if present assign its "type" to a variable. "SubType" is | separated which I and converting to an array using array1..split('|'). This when assigning to a variable will need to be comma separated. I tried using array filter but I am not able to find a way to loop thorough the second array. Can anyone help?
array1.forEach(reg => {
if (reg.SubType) {
let subTypeTemp = reg.SubType.split('|');
let tempVariable = subTypeTemp.some(ele => {
let stringToassign = '';
for (let i = 0; i < array2.length; i++) {
if (ele == array2[i].id) {
stringToassign += array2[i].type + ",";
}
}
})
}
})
const array1 = [
{
SubType: "2|3|4|5|6"
},
{ SubType: "2" },
{ SubType: "3|4" },
{ SubType: "6" },
{ SubType: "3|6" }
];
const array2 = [
{
id: 2,
type: "1"
},
{ id: 3, type: "5" },
{ id: 4, type: "4" },
{ id: 5, type: "3" },
{ id: 6, type: "2" }
];
const array2Obj = array2.reduce(
(acc, curr) => ({
...acc,
[curr.id]: curr.type
}),
{}
);
const types = [];
array1.forEach(item => {
const sub_types = item.SubType.split("|");
sub_types.forEach(st => {
if (st in array2Obj) {
types.push(array2Obj[st]);
}
});
});
const types_str = [...new Set(types)].join(',');
console.log("types", types_str);
You could take a Map and prevent looping array2 over and over for getting type of a wanted id.
var array1 = [{ SubType: "2|3|4|5|6" }, { SubType: "2" }, { SubType: "3|4" }, { SubType: "6" }, { SubType: "3|6" }],
array2 = [{ id: 2, type: "1" }, { id: 3, type: "5" }, { id: 4, type: "4" }, { id: 5, type: "3" }, { id: 6, type: "2" }],
types = new Map(array2.map(({ id, type }) => [id.toString(), type])),
result = array1.map(({ SubType }) => SubType
.split('|')
.map(Map.prototype.get, types)
.join()
);
console.log(result);

What's the most efficent way to populate an a property of an array of objects with a larger array with data?

I have a small array of objects with properties, like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [ <PLACE VALUES HERE> ],
},
{
topicName: 'Cost',
topic: 2,
dates: [ <PLACE VALUES HERE> ],
},
];
Then I have a large array of objects that I wish to extract some of the properties from in to the above dates array.
Here's what the data I wish to extract from:
[
{
"date": "2014-02-01",
"device": "Computer",
"match-type": "NA",
"clicks": 66,
"revenue": 1037,
"conversions": 2,
"cost": 284.35,
"impressions": 5330,
"ROI": 3.64691401441885
},
{
"date": "2014-02-01",
"device": "Tablet",
"match-type": "NA",
"clicks": 38,
"revenue": 587,
"conversions": 2,
"cost": 194.01000000000005,
"impressions": 1934,
"ROI": 3.025617236224936
},
{
"date": "2014-02-02",
"device": "Tablet",
"match-type": "NA",
"clicks": 40,
"revenue": 587,
"conversions": 2,
"cost": 190,
"impressions": 1934,
"ROI": 3.025617236224936
},
]
Now I need the data from all of the members of the last array and insert that releveant data for the particular object in the first array (totalling where necessary), like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [
{
date: '2014-02-01',
value: 104
},
{
date: '2014-02-02',
value: 40
}
],
},
{
topicName: 'Cost',
topic: 2,
dates: [
{
date: '2014-02-01',
value: 284,3519401
},
{
date: '2014-02-02',
value: 190
}
],
},
];
The target is the latest version of Chrome and I'm using Webpack with Babel so all the latest stuff is available.
Assuming the last dataset can be pretty large, what's the most efficient way to go about this?
[EDIT]
This is what I've come up with so far:
const dataAdapter = rawData => {
const topics = ['clicks', 'revenue', 'cost', 'roi'];
const topicsData = topics.map((topic, index) => {
const thisTopic = {};
thisTopic.topicName = topic;
thisTopic.topic = index;
thisTopic.dates = [];
return thisTopic;
});
const convertedData = topicsData.map(topicData => {
const thisTopic = topicData;
const map = new Map();
rawData.forEach(elem => {
map.set(elem.date, (map.get(elem.date) || 0) + elem[[thisTopic.topicName]]);
});
thisTopic.dates = Array.from(map);
return thisTopic;
});
return convertedData;
};
Thanks,
/J
You could take an object as reference to the wanted keys and date. Then iterate data and check if a reference to a result set exists. If not, create a new result set.
var result = [{ topicName: 'Clicks', topic: 1, dates: [], }, { topicName: 'Cost', topic: 2, dates: [], }],
data = [{ date: "2014-02-01", device: "Computer", "match-type": "NA", clicks: 66, revenue: 1037, conversions: 2, cost: 284.35, impressions: 5330, ROI: 3.64691401441885 }, { date: "2014-02-01", device: "Tablet", "match-type": "NA", clicks: 38, revenue: 587, conversions: 2, cost: 194.01000000000005, impressions: 1934, ROI: 3.025617236224936 }, { date: "2014-02-02", device: "Tablet", "match-type": "NA", clicks: 40, revenue: 587, conversions: 2, cost: 190, impressions: 1934, ROI: 3.025617236224936 }],
hash = { clicks: { _: result[0].dates }, cost: { _: result[1].dates }, };
data.forEach(function (o) {
['clicks', 'cost'].forEach(function (k) {
if (!hash[k][o.date]) {
hash[k][o.date] = { date: o.date, value: o[k] };
hash[k]._.push(hash[k][o.date]);
return;
}
hash[k][o.date].value += o[k];
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming your data is in data variable and topics are in topics variable. This solution uses only javascript builtin objects.
const getPropDateMap = (obj, prop) => obj
.reduce((accObj, item) => {
return Object.assign(accObj, {
[item.date]: item.clicks + (accObj[item.date] || 0)
})
}, {})
topics.forEach(topic => {
topic.dates = Object
.entries(getPropDateMap(data, topic.topicName.toLowerCase()))
.map(entry => ({date: entry[0], value: entry[1]}))
})

Merging array items with null values considered

I have an array with data from different tables (at least 9 tables).I want to iterate through the array and kind of merge them in a way that the first item is taken from all arrays, then second and so on.
I'll better explain now with an example below.
So suppose I have an input array like the below :
var arrData = [{
"Id": 1,
"Name": 'Ken',
"Age": '',
"Hobbies": [{
'HobbyId': 1,
'HobbyName': 'Swimming'
}, {
'HobbyId': 2,
'HobbyName': 'Reading'
}],
"Skills": [{
'SkillId': 1,
'SkillName': 'PHP'
}, {
'SkillId': 2,
'SkillName': 'MySQL'
}],
"Language": [{
'LangId': 2,
'LangName': 'English'
}, {
'LangId': 3,
'LangName': 'Chinese'
}]
},
{
"Id": 2,
"Name": 'Mike',
"Age": '20',
"Hobbies": [],
"Skills": [],
"Language": []
},
{
"Id": 3,
"Name": 'Charlie',
"Age": '25',
"Hobbies": [{
'HobbyId': 5,
'HobbyName': 'Dance'
}, {
'HobbyId': 6,
'HobbyName': ''
}, {
'HobbyId': 7,
'HobbyName': 'Writing'
}],
"Skills": [],
"Language": [{
'LangId': 7,
'LangName': 'English'
}]
}
];
console.log(arrData);
My resulting array should be something as below :
[
[1,"Ken",'',1,'Swimming',1,'PHP',2,'English'],
[1,"Ken",'',2,'Reading',2,'MySQL',3,'Chinese'],
[2,"Mike",'20','','','','','',''],
[3,"Charlie",'25','5','Dance','','',7,'English'],
[3,"Charlie",'25','6','','','','',''],
[3,"Charlie",'25','7','Writing','','','',''],
]
Here you may notice that the loop for an item iterates for the maximum number of times found in array for that item.
So for the 3rd item with Id = 3 and Name = 'Charlie' the loop iterates for 3 times as there are 3 (maximum) item in Hobbies array.
The is just a sample array and in reality I will have 9 arrays and my code should be very dynamic to handle it.
I have achieved 90% of the output but I am only stuck where I should pass empty values for the items not found in the array.
For example in the sample above Mike has no items for Hobbies,Skills or Language, in that case I still have to pass empty values.
Or if you see Charlie has 3 Hobbies, no skills and 1 Language, my array should have 3 items but as the Skills is empty is should have empty values for them and for the second and third item for charlie it should have empty values for Language.
I hope this makes sense.
I have tried some code as below and I'll explain at the bottom.
$(function() {
// Handler for .ready() called.
var finalData = [],
arr = [],
arrHeader = [],
cnt = 0,
generateMaxRows = 0;
//finalData.push({"Id":"","Name":'',"Age" : '','HobbyId':'Hobbies','HobbyName':'', 'SkillId':'Skills','SkillName':'','LangId':'Language','LangName':''});
var arrHeader = [{
"Id": "Id",
"Name": 'Name',
"Age": 'Age',
"Hobbies": [{
'HobbyId': 'HobbyId',
'HobbyName': 'HobbyName'
}],
"Skills": [{
'SkillId': 'SkillId',
'SkillName': 'SkillName'
}],
"Language": [{
'LangId': 'LangId',
'LangName': 'LangName'
}]
}];
var arrData = [{
"Id": 1,
"Name": 'Ken',
"Age": '',
"Hobbies": [{
'HobbyId': 1,
'HobbyName': 'Swimming'
}, {
'HobbyId': 2,
'HobbyName': 'Reading'
}],
"Skills": [{
'SkillId': 1,
'SkillName': 'PHP'
}, {
'SkillId': 2,
'SkillName': 'MySQL'
}],
"Language": [{
'LangId': 2,
'LangName': 'English'
}, {
'LangId': 3,
'LangName': 'Chinese'
}]
},
{
"Id": 2,
"Name": 'Mike',
"Age": '20',
"Hobbies": [],
"Skills": [],
"Language": []
},
{
"Id": 3,
"Name": 'Charlie',
"Age": '25',
"Hobbies": [{
'HobbyId': 5,
'HobbyName': 'Dance'
}, {
'HobbyId': 6,
'HobbyName': ''
}, {
'HobbyId': 7,
'HobbyName': 'Writing'
}],
"Skills": [],
"Language": [{
'LangId': 7,
'LangName': 'English'
}]
}
];
jQuery.each(arrData, function(key, val) {
arr = [];
generateMaxRows = 1;
jQuery.each(val, function(k, v) {
if (Array.isArray(v)) {
generateMaxRows = (v.length > generateMaxRows) ? v.length : generateMaxRows;
}
});
for (var row = 0; row < generateMaxRows; row++) {
arr = [];
jQuery.each(arrHeader[0], function(header_key, header_val) {
jQuery.each(val, function(row_key, row_val) {
if (row_key == header_val) // Not an array.
{
arr.push(row_val);
} else {
if (Array.isArray(header_val)) {
if (Array.isArray(row_val)) {
jQuery.each(header_val[0], function(head_key, head_val) {
console.log(head_val);
jQuery.each(row_val[row], function(subrow_key, subrow_val) {
if (subrow_key == head_val) {
arr.push(subrow_val);
}
});
});
}
}
}
});
// console.log(arr);
});
finalData.push(arr);
}
});
console.log(JSON.stringify(finalData));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I have done a static array as arrHeader which will have a definition of array items that is to be compared with input array.
I have achieved 90% of the output except the point where I have to pass null values where the arrHeader has an item but arrData does not.
Below is my real example helper array(arrHeader).
var arrHeader= {
"ItemMasterNumber": "ItemMasterNumber",
"IM_Description": "IM_Description",
"IM_FirstProcessDate": "IM_FirstProcessDate",
"IM_Alias": "IM_Alias",
"IM_MasterPrice": "IM_MasterPrice",
"IM_Category": "IM_Category",
"IM_Version": "IM_Version",
"IM_Type": "IM_Type",
"ItemProducts" : [{
"PO_Id" : "PO_Id",
"PO_Code" : "PO_Code",
"PO_Name" : "PO_Name",
"PO_Type" : "PO_Type",
"PO_TaxClassification" : "PO_TaxClassification",
"PO_Bundle" : "PO_Bundle",
"PO_PricingMechanism" : "PO_PricingMechanism",
"PO_StoreVisible" : "PO_StoreVisible",
"PO_NotSoldSeparately" : "PO_NotSoldSeparately",
"PO_EnableDate" : "PO_EnableDate",
"PO_AvailaibilityRule" : "PO_AvailaibilityRule",
"PO_DisableDate" : "PO_DisableDate",
"PO_MinOrderQty" : "PO_MinOrderQty",
"PO_ExpectedReleaseDate" : "PO_ExpectedReleaseDate",
"PO_Description" : "PO_Description",
"PO_GrantOfferingType" : "PO_GrantOfferingType"
}],
"ItemFeatureSet": [{
"FS_Id": "FS_Id",
"FS_Code": "FS_Code",
"FS_Name": "FS_Name",
"FS_Description": "FS_Description",
"FS_EnablementType": "FS_EnablementType"
}],
"ItemFeatures": [{
"FE_Id": "FE_Id",
"FE_Name": "FE_Name",
"FE_Value": "FE_Value"
}],
"ItemCharges": [{
"CH_ChargeId": "CH_ChargeId",
"CH_Name": "CH_Name",
"CH_Description": "CH_Description",
"CH_Type": "CH_Type",
"CH_ProvisioningTag": "CH_ProvisioningTag",
"CH_Currency": "CH_Currency",
"CH_CustomerSegment": "CH_CustomerSegment",
"CH_ExtendedCustomerSegment": "CH_ExtendedCustomerSegment",
"CH_Region": "CH_Region",
"CH_Active": "CH_Active"
}],
"ItemChargeAttributes": [{
"CA_Id": "CA_Id",
"CA_ListPrice": "CA_ListPrice",
"CA_FairValueBasis": "CA_FairValueBasis",
"CA_FairValueMethod": "CA_FairValueMethod",
"CA_FairValueLow": "CA_FairValueLow",
"CA_FairValueHigh": "CA_FairValueHigh",
"CA_EffectiveStartDate": "CA_EffectiveStartDate"
}],
"ItemPackages": [{
"PA_PackageId": "PA_PackageId"
}],
"ItemPackagesComponents": [{
"PA_ComponentId": "PA_ComponentId",
"PA_Type": "PA_Type",
}],
"ItemOffers": [{
"OF_OfferId": "OF_OfferId",
"OF_Name": "OF_Name",
"OF_Description": "OF_Description",
"OF_Level": "OF_Level",
"OF_Type": "OF_Type",
"OF_CustomerSegment" : "OF_CustomerSegment",
"OF_SalesChannel" : "OF_SalesChannel",
"OF_ListPriceOffer" : "OF_ListPriceOffer",
"OF_Region" : "OF_Region",
"OF_CountryCode" : "OF_CountryCode",
"OF_EffectiveStartDate": "OF_EffectiveStartDate",
"OF_EffectiveEndDate": "OF_EffectiveEndDate"
}],
"ItemOffersComponents": [{
"OC_Id": "OC_Id",
"OC_Quantity": "OC_Quantity",
"OC_AdjustmentAmount": "OC_AdjustmentAmount"
}]
};
Can you please help me out with this?
Any help is very much appreciated.
Thanks!
You could use arrHeader with arrays for nested properties. For zeroes, the actual index is used.
var arrHeader = [
'Id',
'Name',
'Age',
['Hobbies', 0, 'HobbyId'],
['Hobbies', 0, 'HobbyName'],
['Skills', 0, 'SkillId'],
['Skills', 0, 'SkillName'],
['Language', 0, 'LangId'],
['Language', 0, 'LangName']
],
data = [{ Id: 1, Name: "Ken", Age: "", Hobbies: [{ HobbyId: 1, HobbyName: "Swimming" }, { HobbyId: 2, HobbyName: "Reading" }], Skills: [{ SkillId: 1, SkillName: "PHP" }, { SkillId: 2, SkillName: "MySQL" }], Language: [{ LangId: 2, LangName: "English" }, { LangId: 3, LangName: "Chinese" }] }, { Id: 2, Name: "Mike", Age: "20", Hobbies: [], Skills: [], Language: [] }, { Id: 3, Name: "Charlie", Age: "25", Hobbies: [{ HobbyId: 5, HobbyName: "Dance" }, { HobbyId: 6, HobbyName: "" }, { HobbyId: 7, HobbyName: "Writing" }], Skills: [], Language: [{ LangId: 7, LangName: "English" }] }],
result = data.reduce(function (r, a) {
var max = arrHeader.reduce(function (m, k) {
return Array.isArray(k) ? Math.max(m, (a[k[0]] || []).length) : m;
}, 1),
i;
for (i = 0; i < max; i++) {
r.push(arrHeader.map(function (k) {
return Array.isArray(k) ? k.reduce(function (v, l) {
return (v || {})[l || i] || '';
}, a) : a[k];
}));
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories