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 a following data structure, array of dictionaries
[
{
"id": 1,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 2",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
[
{
"id": 1,
"name": "Some Name 2",
"topics": [
{
"id": 1,
"name": "Topic Name 1",
"topics": [],
},
{
"id": 2,
"name": "Topic Name 2",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
I want to essentially be able to combine the arrays within each dictionary if I have the same id, so for each dictionary i will have higher level dictionary items being the same and essentially combine those together to avoid duplication. Preferably i would like it to be recursively being that in case the topics within topics are also duplicate (you can assume the order is always respected).
I would appreciate if you could share a solution to this
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 json data like this:
[{
"Id": 1,
"Name": "Test 1",
"Time": {
"start": "22:00",
"end": "22:30",
"duration": 30
}
}, {
"Id": 2,
"Name": "Test 2",
"Time": {
"start": "22:05",
"end": "22:35",
"duration": 30
}
}, {
"Id": 3,
"Name": "Test 3",
"Time": {
"start": "23:25",
"end": "23:40",
"duration": 15
}
},{
"Id": 4,
"Name": "Test 4",
"Time": {
"start": "22:15",
"end": "22:50",
"duration": 15
}
}
]
And i want get result like this, determine which intersect by Time:
[
["Test 1", "Test 2", "Test 4"],
["Test 3"]
]
I have solution, but it's too complicated and i use 4 loops inside each other
If there is an easier solution, please suggest, thanks!
Try aggregation, I am not sure this will work exact as per your requirement,
$addFields add new field startHour, that splits string hour and minute from string, using $split, and $arrayElemAt return first element means hour
$group by startHour and make array of Name
db.collection.aggregate([
{
$addFields: {
startHour: {
$arrayElemAt: [{ $split: ["$Time.start", ":"] }, 0]
}
}
},
{
$group: {
_id: "$startHour",
Name: { $push: "$Name" }
}
}
])
Playground
Result:
[
{
"Name": ["Test 1","Test 2","Test 4"],
"_id": "22"
},
{
"Name": ["Test 3"],
"_id": "23"
}
]
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
This is JSON for an article
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}
Im currently looking through redux tutorials, they give a helper which normalise JSON data, does anyone know what type of normalisation this is?