I have an array of object that looks like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 1",
"file": "f1.csv"
},
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 2",
"file": "f2.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 1",
"file": "f3.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 2",
"file": "f4.csv"
}
]
Using lodash how to transform this json array to be like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
lists: [{ "name": "devs 1", "file": "f1.csv" }, { "name": "devs 2", "file": "f2.csv" }]
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
lists: [{ "name": "ceos 1", "file": "f3.csv" }, { "name": "ceos 2", "file": "f4.csv" }]
}
]
I tried various solutions but without any luck.
_.groupBy() the group.name, and then _.map() to the desired structure:
const data = [{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 1","file":"f1.csv"},{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 2","file":"f2.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 1","file":"f3.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 2","file":"f4.csv"}];
const result = _.flow([
(arr) => _.groupBy(arr, 'group.name'),
(groups) => _.map(groups, (g) => ({
..._.head(g).group,
lists: _.map(g, o => _.pick(o, ['name', 'file']))
}))
])(data);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could also achieve this without lodash via ES6 with Object.entries, reduce & map:
const data = [{ "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 1", "file": "f1.csv" }, { "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 2", "file": "f2.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 1", "file": "f3.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 2", "file": "f4.csv" } ]
const result = Object.entries(data.reduce((r,c) =>
(r[c.group.name] = [...r[c.group.name] || [], c], r), {}))
.reduce((r,[k,v]) => (r.push({ group: v[0].group, lists: v.map(({name, file}) =>
({ name, file })) }), r), [])
console.log(result)
Related
I have the below array which has a sub array of categories, I would like to output the array over and over but grouping the items into another array based on their related categories
testData2: any = [{
"id": 0,
"name": "XyZ",
"category": [ {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
},
{
"id": 3,
"name": "something 3",
"category": [{
"title": "horse"
}]
}, {
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}, {
"id": 5,
"name": "something 4",
"category": [{
"title": "fishing"
}]
}
]
So far I have this which works, but i cant help wonder if there is some new JS magic which may be more perfomant to accomplish this ?
let newArray = [];
for (let x = 0; x < this.testData2.length; x++) {
let parent = this.testData2[x];
let child = parent.category;
for (let y = 0; y < child.length; y++) {
let cat = child[y];
let format = parent
newArray.push({ group_heading: cat.title, services: [format] })
}
}
let finalOutput = newArray.reduce((acc, curr) => {
const ndx = acc.findIndex((e: any) => e.group_heading === curr.group_heading);
if(ndx > -1){
acc[ndx].services.push(...curr.services)
} else{
acc.push(curr)
}
return acc;
}, [])
which outputs this as desired
[{
"group_heading": "horse",
"services": [{
"id": 0,
"name": "XyZ",
"category": [{
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 3,
"name": "something 3",
"category": [{
"title": "horse"
}]
}]
}, {
"group_heading": "food",
"services": [{
"id": 0,
"name": "XyZ",
"category": [{
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}]
}, {
"group_heading": "fishing",
"services": [{
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 5,
"name": "something 4",
"category": [{
"title": "fishing"
}]
}]
}, {
"group_heading": "beer",
"services": [{
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}]
}]
I would probably do something like this:
// first collect services by category
const servicesByCategory = {}
for(const service of testData2){
for(const {title} of service.category){
if(!servicesByCategory[title]){
servicesByCategory[title] = []
}
servicesByCategory[title].push(data)
}
}
// whip it into whatever form you need
return Object.entries(servicesByCategory)
.map(([group_headings, services]) => ({group_headings, services}))
I have the main three parameters that are important category, subcategory, and group
This is an array of objects and I want to change the format of this array
Like all data divide into category inside sub-category inside group wise data I want
const data =
[
{
"type": "checkbox",
"value": true,
"category": "id",
"subcategory": "id document 1",
"group": "group1"
},
{
"type": "radio",
"value": 2,
"category": "id",
"subcategory": "id document 2",
"group": "group2"
},
{
"type": "radio",
"value": 3,
"category": "id",
"subcategory": "id document 2",
"group": "group2"
},
{
"type": "string",
"value": "dfgdfg",
"category": "Services",
"subcategory": "Service A",
"group": "g1"
},
{
"type": "string",
"value": "fxs",
"category": "Services",
"subcategory": "Service A",
"group": "g1"
},
{
"type": "string",
"value": "3",
"category": "Services access",
"subcategory": "Service B",
"group": "g1"
},
{
"type": "string",
"value": "sgredfg25ghfghrt54645dfeh",
"category": "Services access",
"subcategory": "Service B",
"group": "g2"
},
{
"type": "string",
"value": "sgredfg25ghfghrt54645dfeh",
"category": "Services access",
"subcategory": "Service C",
"group": "g3"
}
]
The expected result looks like this
const data = [
{
"category": "id",
"subcategory": [
{
"subcategory_name": "id document 1",
"subcategory_data": [
{
"group": "group1",
"groupdata": [
{
"type": "checkbox",
"value": true,
"category": "id",
"subcategory": "id document 1",
"group": "group1"
}
]
}
]
},
{
"subcategory_name": "id document 2",
"subcategory_data": [
{
"group": "group1",
"groupdata": [
{
"type": "checkbox",
"value": true,
"category": "id",
"subcategory": "id document 2",
"group": "group1"
},
{
"type": "checkbox",
"value": true,
"category": "id",
"subcategory": "id document 2",
"group": "group1"
}
]
}
]
}
]
},
{
"category": "Services",
"subcategory": [
{
"subcategory_name": "Service B",
"subcategory_data": [
{
"group": "c1",
"groupdata": [
{
"type": "checkbox",
"value": true,
"category": "Services",
"subcategory": "Service B",
"group": "c1"
}
]
},
{
"group": "c2",
"groupdata": [
{
"type": "checkbox",
"value": true,
"category": "Services",
"subcategory": "Service B",
"group": "c2"
}
]
}
]
},
{
"subcategory_name": "Service C",
"subcategory_data": [
{
"group": "f1",
"groupdata": [
{
"type": "checkbox",
"value": true,
"category": "Services",
"subcategory": "Service C",
"group": "f1"
},
{
"type": "checkbox",
"value": true,
"category": "Services",
"subcategory": "Service C",
"group": "f1"
}
]
}
]
}
]
}
]
If the category match data push into the match subcategory if not then create a category for the same and if the subcategory match push into that one otherwise create a new one same as the group if the group match into subcategory then push otherwise create one
Like I want hierarchy like category => subcategory => group
And I try the this code
let finaldata = []
let flag1;
let aa = []
for (let i = 0; i < parseData.length; i++) {
if (i == 0) {
finaldata.push({ category: parseData[i].category, subcategory: [parseData[i]] })
flag1 = true
} else {
if (parseData[i - 1]?.category !== parseData[i].category) {
finaldata.push({ category: parseData[i].category, subcategory: [parseData[i]] })
} else {
aa = finaldata.map(x => x.category === parseData[i].category ? { ...x, subcategory: [...x.subcategory, parseData[i]] } : x);
}
}
if (!flag1) break;
}
does this helps?
const data =
[
{
"type": "checkbox",
"value": true,
"category": "id",
"subcategory": "id document 1",
"group": "group1"
},
{
"type": "radio",
"value": 2,
"category": "id",
"subcategory": "id document 2",
"group": "group2"
},
{
"type": "radio",
"value": 3,
"category": "id",
"subcategory": "id document 2",
"group": "group2"
},
{
"type": "string",
"value": "dfgdfg",
"category": "Services",
"subcategory": "Service A",
"group": "g1"
},
{
"type": "string",
"value": "fxs",
"category": "Services",
"subcategory": "Service A",
"group": "g1"
},
{
"type": "string",
"value": "3",
"category": "Services access",
"subcategory": "Service B",
"group": "g1"
},
{
"type": "string",
"value": "sgredfg25ghfghrt54645dfeh",
"category": "Services access",
"subcategory": "Service B",
"group": "g2"
},
{
"type": "string",
"value": "sgredfg25ghfghrt54645dfeh",
"category": "Services access",
"subcategory": "Service C",
"group": "g3"
}
]
const result = data.reduce((p, c) => {
const i = p.findIndex(p => p.category === c.category)
if (i !== -1) {
const j = p[i].subcategory.findIndex(s => s.subcategory_name === c.subcategory);
if (j !== -1) {
const k = p[i].subcategory[j].subcategory_data.findIndex(sd => sd.group === c.group);
if (k !== -1) {
p[i].subcategory[j].subcategory_data[k].groupdata.push(c);
return p;
}
p[i].subcategory[j].subcategory_data.push({
group: c.group,
groupdata: [
c
]
})
return p;
}
p[i].subcategory.push({
subcategory_name: c.subcategory,
subcategory_data: [
{
group: c.group,
groupdata: [
{
...c
}
]
}
]
})
return p
}
p.push({
category: c.category,
subcategory: [
{
subcategory_name: c.subcategory,
subcategory_data: [
{
group: c.group,
groupdata: [
c
]
}
]
}
]
})
return p;
}, []);
console.log(result);
Another approach to group items:
const data = [{"type":"checkbox","value":true,"category":"id","subcategory":"id document 1","group":"group1"},{"type":"radio","value":2,"category":"id","subcategory":"id document 2","group":"group2"},{"type":"radio","value":3,"category":"id","subcategory":"id document 2","group":"group2"},{"type":"string","value":"dfgdfg","category":"Services","subcategory":"Service A","group":"g1"},{"type":"string","value":"fxs","category":"Services","subcategory":"Service A","group":"g1"},{"type":"string","value":"3","category":"Services access","subcategory":"Service B","group":"g1"},{"type":"string","value":"sgredfg25ghfghrt54645dfeh","category":"Services access","subcategory":"Service B","group":"g2"},{"type":"string","value":"sgredfg25ghfghrt54645dfeh","category":"Services access","subcategory":"Service C","group":"g3"}];
const groupByToPairs = (items, key) => Object.entries(items.reduce((acc, item) => {
acc[item[key]] ??= [];
acc[item[key]].push(item);
return acc;
}, {}));
const makeGroups = (items) => groupByToPairs(items, 'group')
.map(([group, groupdata]) => ({ group, groupdata }));
const makeSubcategories = (items) => groupByToPairs(items, 'subcategory')
.map(([subcategory_name, items]) => ({ subcategory_name, subcategory_data: makeGroups(items) }));
const makeCategories = (items) => groupByToPairs(items, 'category')
.map(([category, items]) => ({ category, subcategory: makeSubcategories(items) }));
console.log(makeCategories(data));
.as-console-wrapper { max-height: 100% !important; top: 0 }
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
I have tried the below solution for question2 to get result2. What would be a more efficient way to get both the result (result1, result2 & result3) with the same code?
question1 = [
{
"groupName": "Group 1",
"id": "group1",
"options": [
{
"name": "Cat1",
"selected": true
},
{
"name": "Cat2",
"selected": true
},
{
"name": "Cat3",
"selected": false
},
{
"name": "Cat4",
"selected": false
}
]
},
{
"groupName": "Group 2",
"id": "Brand",
"options": [
{
"name": "brand1",
"selected": false
},
{
"name": "brand2",
"selected": true
},
{
"name": "brand3",
"selected": false
}
]
},
{
"groupName": "Group 3",
"id": "Price",
"options": [
{
"name": "$0 - $9",
"selected": false
},
{
"name": "$9 - $19",
"selected": false
},
{
"name": "$20 - $29",
"selected": false
}
],
"range": {
"min": 5,
"max": 20
}
}
]
result1 = [{
"groupName": "Group 1",
"id": "group1",
"name": "Cat1",
},
{
"groupName": "Group 1",
"id": "group1",
"name": "Cat2",
},
{
"groupName": "Group 2",
"id": "Brand",
"name": "brand2",
},
{
"groupName": "Group 3",
"id": "Price",
"min": 5,
"max": 20
}
]
I have a solution for question2, although I know it's not very efficient, so what could be a better solution?
question2 = [
{
"groupName": "Group 1",
"id": "group1",
"options": [
{
"name": "Cat1",
"selected": true
},
{
"name": "Cat2",
"selected": false
},
{
"name": "Cat3",
"selected": false
},
{
"name": "Cat4",
"selected": false
}
]
},
{
"groupName": "Group 2",
"id": "Brand",
"options": [
{
"name": "brand1",
"selected": false
},
{
"name": "brand2",
"selected": true
},
{
"name": "brand3",
"selected": false
}
]
},
{
"groupName": "Group 3",
"id": "Price",
"options": [
{
"name": "$0 - $9",
"selected": true
},
{
"name": "$9 - $19",
"selected": false
},
{
"name": "$20 - $29",
"selected": false
}
],
"range": {
"min": null,
"max": null
}
}
]
const selected2 = question2.map(group => group.options.filter(option => option.selected).map(option => ({groupName: group.groupName, id: group.id, name: option.name}))).flat(1)
console.log(selected2)
For question3, group 1, none of the options is selected and correspondingly I should get result3. So, only if any of the options are selected or the price has a range (either min/max/both min&max), it is displayed in the result.
question3 = [
{
"groupName": "Group 1",
"id": "group1",
"options": [
{
"name": "Cat1",
"selected": false
},
{
"name": "Cat2",
"selected": false
},
{
"name": "Cat3",
"selected": false
},
{
"name": "Cat4",
"selected": false
}
]
},
{
"groupName": "Group 2",
"id": "Brand",
"options": [
{
"name": "brand1",
"selected": false
},
{
"name": "brand2",
"selected": true
},
{
"name": "brand3",
"selected": false
}
]
},
{
"groupName": "Group 3",
"id": "Price",
"options": [
{
"name": "$0 - $9",
"selected": false
},
{
"name": "$9 - $19",
"selected": false
},
{
"name": "$20 - $29",
"selected": false
}
],
"range": {
"min": 5,
"max": 20
}
}
]
result3 = [
{
"groupName": "Group 2",
"id": "Brand",
"name": "brand2",
},
{
"groupName": "Group 3",
"id": "Price",
"min": 5,
"max": 20
}
]
I want to use reduce to get these above (result1, result2 & result3) solutions. Could someone help me with this?
Edited to check scenario where two are selected in the same group, solution resulting in result1.
When multiple selected trues found in the accumulator object I'm adding a key where keyname is a combination of groupName and selected name. If no selected true but has max or min values the key name will be just groupName. I take Object.values() of the final object to get the array of values.
const question1 = [{"groupName": "Group 1","id": "group1","options": [{"name": "Cat1","selected": true},{"name": "Cat2","selected": true},{"name": "Cat3","selected": false},{"name": "Cat4","selected": false}]},{"groupName": "Group 2","id": "Brand","options": [{"name": "brand1","selected": false},{"name": "brand2","selected": true},{"name": "brand3","selected": false}]},{"groupName": "Group 3","id": "Price","options": [{"name": "$0 - $9","selected": false},{"name": "$9 - $19","selected": false},{"name": "$20 - $29","selected": false}],"range": {"min": 5,"max": 20}}]
const question2 = [{"groupName": "Group 1","id": "group1","options":[{"name":"Cat1","selected": true},{"name": "Cat2","selected": false},{"name":"Cat3","selected": false},{"name": "Cat4","selected": false}]},{"groupName": "Group 2","id": "Brand","options": [{"name":"brand1","selected":false},{"name": "brand2","selected": true},{"name": "brand3","selected": false}]},{"groupName": "Group 3","id": "Price","options": [{"name": "$0 -$9","selected": true},{"name": "$9 - $19","selected": false},{"name": "$20 -$29","selected": false}],"range": {"min": null,"max": null}}]
const question3 = [{"groupName": "Group 1","id": "group1","options": [{"name": "Cat1","selected": false},{"name": "Cat2","selected": false},{"name": "Cat3","selected": false},{"name": "Cat4","selected": false}]},{"groupName": "Group 2","id": "Brand","options": [{"name": "brand1","selected": false},{"name": "brand2","selected": true},{"name": "brand3","selected": false}]},{"groupName": "Group 3","id": "Price","options": [{"name": "$0 - $9","selected": false},{"name": "$9 - $19","selected": false},{"name": "$20 - $29","selected": false}],"range": {"min": 5,"max": 20}}]
const formatter = (arr) => {
return Object.values(arr.reduce((acc,curr) => {
const hasMax = curr.range && curr.range.max;
const hasMin = curr.range && curr.range.max
const filtered = curr['options'].filter((option) => option.selected);
if (filtered.length) {
filtered.forEach((el) => {
acc[curr.groupName+el.name] = {id: curr.id,groupName: curr.groupName,name: el.name}
})
}
else if (hasMin || hasMax){
acc[curr.groupName] = {id: curr.id,groupName: curr.groupName}
if(hasMax) acc[curr.groupName]['max'] = curr.range.max
if(hasMin) acc[curr.groupName]['min'] = curr.range.min
}
return acc;
},{}))
}
const result1 = formatter(question1)
const result2 = formatter(question2)
const result3 = formatter(question3)
console.log(result1)
console.log(result2)
console.log(result3)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have two arrays that have a structure given below.
I need to combine the values of two arrays into one and the order should be in sequence. I need to keep the other pockets too.
The header should be first then the child and then the next header then the next child's pockets.
const header = {
"list": [{
"name": "header-a",
"id": "id-a"
}, {
"name": "header-b",
"id": "id-b"
}]
};
const child = {
"list": [{
"name": "child",
"id": "c1",
'type': "id-a"
},
{
"name": "child 2",
"id": "c2",
'type': "id-a"
},
{
"name": "child 4",
"id": "c4",
'type': "id-b"
},
{
"name": "child-5",
"id": "c5",
'type': "id-b"
}]
};
expected result is
const result = {
"list": [{
"name": "header-a",
"header": true,
},
{
"name": "child",
"id": "c1",
'type': "id-a"
},
{
"name": "child 2",
"id": "c2",
'type': "id-a"
},
{
"name": "header-b",
"header": true,
},
{
"name": "child 4",
"id": "c4",
'type': "id-b"
},
{
"name": "child-5",
"id": "c5",
'type': "id-b"
}]
};
You loop through the headers and for each header, you push into the "combined" array an item like { name: header.name, header: true }, then filter all children whose type is equal to the header id and push them too into the array:
const header = {
"list": [{
"name": "header-a",
"id": "id-a"
}, {
"name": "header-b",
"id": "id-b"
}]
};
const child = {
"list": [{
"name": "child",
"id": "c1",
'type': "id-a"
},
{
"name": "child 2",
"id": "c2",
'type': "id-a"
},
{
"name": "child 4",
"id": "c4",
'type': "id-b"
},
{
"name": "child-5",
"id": "c5",
'type': "id-b"
}
]
};
const combined = { list: [] };
header.list.forEach(h => {
combined.list.push({ name: h.name, header: true });
child.list.filter(c => c.type === h.id).forEach(c => combined.list.push(c));
});
console.log(combined);
I have 2 arrays objects and I need to do a count of home many types of card we have.
The first object contains all the car id's and the second list contains the types of cars.
Here is the data:
var arr = {
"categories": [{
"id": "100",
"name": "category name",
"car_id": "1"
}, {
"id": "192",
"name": "category name here",
"car_id": "25"
}, {
"id": "192",
"name": "category name here",
"car_id": "27"
}]
};
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
There's only 5 types of cars so I have 5 variables:
var:
ford,
fiat,
mazda,
mini,
mg
So, what I need to end up with is something like this:
ford: 2;
fiat: 1;
mazda: 0;
mini: 0;
mg: 0;
How can I do this?
If your number of types are fixed, then try this approach
Make an map first
var map = {
ford: 0,
fiat: 0,
mazda: 0,
mini: 0,
mg: 0
};
Now iterate the arrays and count by types
arr2.cars.forEach( function( item ){
map[ item.type ]++;
});
your map is populated with the values now.
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
var map = {
ford: 0,
fiat: 0,
mazda: 0,
mini: 0,
mg: 0
};
arr2.cars.forEach(function(item) {
map[item.type] ++;
});
console.log(map);
var arr = {
"categories": [{
"id": "100",
"name": "category name",
"car_id": "1"
}, {
"id": "192",
"name": "category name here",
"car_id": "25"
}, {
"id": "192",
"name": "category name here",
"car_id": "27"
}]
};
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
var carCount, typeCount;
arr.categories.forEach(function(item){
if(item.hasOwnProperty("car_id")){
carCount = arr.categories.length;
}
});
arr2.cars.forEach(function(item){
if(item.hasOwnProperty("type")){
typeCount = arr2.cars.length;
}
});
console.log(carCount);
console.log(typeCount);
https://jsfiddle.net/Law7rzc2/
All you need is
countBy(arr2.cars, 'type')
The implementation of countBy is left as an exercise.
Array.prototype.reduce is your go-to for this kind of array computation. A Map will help you keep track of unique car makes as you iterate thru the array of cars.
var obj2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
const typeStat = cars => {
let map = cars.reduce((m, {type}) =>
m.set(type, (m.get(type) || 0) + 1), new Map());
return Array.from(map, ([make, count]) => ({make, count}));
};
let stats = typeStat(obj2.cars)
console.log(stats);
Output
[
{
"make": "ford",
"count": 2
},
{
"make": "fiat",
"count": 1
}
]