I have an object, which I need to turn into a list "grouped" by a child element. The current object is something like:
{
"results": [
{
"id": 14,
"name": "Productivity",
"category": {
"id": 4,
"name": "Work",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 15,
"name": "Focus",
"category": {
"id": 4,
"name": "Work",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 16,
"name": "Happiness",
"category": {
"id": 5,
"name": "Mood",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 17,
"name": "Positivity",
"category": {
"id": 5,
"name": "Mood",
"color": "#fff",
"color2": "#999"
}
}
]
}
What I'm trying to accomplish is to sort and group results by the category.name key, ie:
[
{
"name": "Work",
"color": "#fff",
"color2": "#999",
"packs": [
{
"id": 14,
"name": "Productivity"
},
{
"id": 15,
"name": "Focus"
}
]
},
{
"name": "Mood",
"color": "#fff",
"color2": "#999",
"packs": [
{
"id": 16,
"name": "Happiness"
},
{
"id": 17,
"name": "Positivity"
}
]
}
]
And so on in such a way that I can loop over the result and build some sort of list.
I'm pretty sure there's a way to accomplish this with either map or reduce, but even though I've had some progress, I just can't seem to order the results in the way I need. (Pretty new to R/RN).
What I'm trying to accomplish in the long run is to be able to pass said info to a looped Component such as
<CategoryList
key={name}
title={name}
results={packs}
/>
Thanks in advance!
Use a Map with the category names as keys and new objects as values to group all the items by category.
When done grouping get the Map values and spread to array
const cats = data.results.reduce((a, {id, name, category:cat})=>{
const obj = a.get(cat.name) || {...cat, packs: []};
obj.packs.push({id, name});
return a.set(cat.name, obj);
}, new Map);
const res = [...cats.values()];
console.log(res)
.as-console-wrapper { max-height: 100%!important;}
<script>
const data={results:[{id:14,name:"Productivity",category:{id:4,name:"Work",color:"#fff",color2:"#999"}},{id:15,name:"Focus",category:{id:4,name:"Work",color:"#fff",color2:"#999"}},{id:16,name:"Happiness",category:{id:5,name:"Mood",color:"#fff",color2:"#999"}},{id:17,name:"Positivity",category:{id:5,name:"Mood",color:"#fff",color2:"#999"}}]};
</script>
var data = {
"results": [
{
"id": 14,
"name": "Productivity",
"category": {
"id": 4,
"name": "Work",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 15,
"name": "Focus",
"category": {
"id": 4,
"name": "Work",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 16,
"name": "Happiness",
"category": {
"id": 5,
"name": "Mood",
"color": "#fff",
"color2": "#999"
}
},
{
"id": 17,
"name": "Positivity",
"category": {
"id": 5,
"name": "Mood",
"color": "#fff",
"color2": "#999"
}
}
]
};
var processedData = [];
data['results'].forEach(result => {
var pack = {
'id': result['id'],
'name': result['name']
};
var category = processedData.find(element => element['name'] === result['category']['name']);
if (category) {
category['packs'].push(pack);
} else {
category = {
'name': result['category']['name'],
'color': result['category']['color'],
'color2': result['category']['color2'],
'packs': [pack]
};
processedData.push(category);
}
});
console.log(processedData);
Related
In this link there is an example of pie chart created by Zoomcharts. To change the color of label outside of the slice I can use styleFunction to modify slice option.
styleFunction: function (slice, data) {
slice.label.textStyle.fillColor = 'red';
},
This only changes the color of letter. I would like to change color of line that connects slice to its label, too. Thanks.
You can try with this
slice: {
connectorStyle: {
lineColor: 'red' // Color you to give to the line of the chart connection
}
}
for more you can check in
https://zoomcharts.com/developers/en/pie-chart/api-reference/settings/slice/connectorStyle.html
Hope you will get the answer.
Check the solution
var data = {
"subvalues": [
{
"value": 50, "name": "Apples", "subvalues": [
{ "value": 25, "name": "Red apples" },
{ "value": 15, "name": "Yellow apples" },
{ "value": 10, "name": "Green apples" }]
},
{
"value": 30, "name": "Oranges", "subvalues": [
{ "value": 10, "name": "Big oranges" },
{ "value": 9, "name": "Small oranges" },
{ "value": 7, "name": "Green oranges" },
{ "value": 4, "name": "Pink oranges" }]
},
{
"value": 20, "name": "Grapes", "subvalues": [
{ "value": 15, "name": "Sweet grapes" },
{ "value": 5, "name": "Sour grapes" }]
},
{ "value": 50, "name": "Vegetables", style: { fillColor: "yellow" } }]
};
var t = new PieChart({
container: document.getElementById("demo"),
area: { height: 350 },
data: { preloaded: data },
slice: {
styleFunction: function (slice, data) {
slice.label.textStyle.fillColor = 'red';
},
connectorStyle: {
lineColor: 'red'
}
}
});
<script src="https://cdn.zoomcharts-cloud.com/1/latest/zoomcharts.js"></script>
<div id="demo"></div>
I've got the following meetings object :
[
{
"id": 19,
"duration": 1.75,
"Employee": {
"name": "Jeanne",
}
},
{
"id": 20,
"duration": 1.00,
"Employee": {
"name": "Louis",
}
},
{
"id": 21,
"duration": 1.00,
"Employee": {
"name": "Jeanne",
}
}
]
I want to group it by Employee.name. Using reduce() here is what I come up with :
meetings.reduce(function (r, a) {
r[a.Employee.name] = r[a.Employee.name] || [];
r[a.Employee.name].push(a);
return r;
}
The resulting object is the following :
{
"Jeanne": [
{
"id": 19,
"duration": 1.75,
"Employee": {
"name": "Jeanne"
}
},
{
"id": 21,
"duration": 1.00,
"Employee": {
"name": "Jeanne"
}
}
],
"Louis": [
{
"id": 20,
"duration": 1.00,
"Employee": {
"name": "Louis"
}
}
]
}
If I try to map() or forEach() i cannot get the value of the element :
Array.from(thisMeeting).forEach(element => console.log(element));
return `undefined`;
Array.from-ming an Object will result in an empty array.
You'll have to iterate over the objects keys with Object.entries(thisMeeting).forEach… and grab the values inside that.
I'm trying to group a big nested object with multiple properties such as this one :
[
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 70,
"name": "Name70"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 61,
"name": "Name61"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 29,
"name": "Name29"
}
}
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 8,
"name": "Name8",
"sub": {
"id": 163,
"name": "Name163"
}
}
}
}
},
{
"id": 10,
"name": "Name10",
"sub": {
"id": 4,
"name": "Name4"
}
}
]
As you can see, the "sub" are not arrays as of now, but they would be in the expected output even if there's only one object in it.
I'd like to group the array by object's id recursively to get this kind of output :
[
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": [
{
"id": 70,
"name": "Name70"
},
{
"id": 61,
"name": "Name61"
},
{
"id": 4,
"name": "Name4",
"sub": [
{
"id": 5,
"name": "Name5",
"sub": [
{
"id": 29,
"name": "Name29"
},
{
"id": 8,
"name": "Name8",
"sub": [
{
"id": 163,
"name": "Name163"
}
]
}
]
}
]
}
]
},
{
"id": 10,
"name": "Name10",
"sub": [
{
"id": 4,
"name": "Name4"
}
]
}
]
So far, I tried some shenanigans with lodash and d3.nest() but I just can't seem to group it.
Have you guys ever face something similar? And if so, how did you manage to code this?
Thanks a lot
You could take a recursive approach with a function which merges an object into an array by looking for same id.
const
merge = (target, { sub, ...o }) => {
let temp = target.find(({ id }) => id === o.id);
if (sub) sub = merge(temp?.sub || [], sub)
if (!temp) target.push(temp = { ...o, sub });
return target;
};
var data = [{ id: 14, name: "Name14", theme: true, sub: { id: 70, name: "Name70" } }, { id: 14, name: "Name14", theme: true, sub: { id: 61, name: "Name61" } }, { id: 14, name: "Name14", theme: true, sub: { id: 4, name: "Name4", sub: { id: 5, name: "Name5", sub: { id: 29, name: "Name29" } } } }, { id: 14, name: "Name14", theme: true, sub: { id: 4, name: "Name4", sub: { id: 5, name: "Name5", sub: { id: 8, name: "Name8", sub: { id: 163, name: "Name163" } } } } }, { id: 10, name: "Name10", sub: { id: 4, name: "Name4" } }],
result = data.reduce(merge, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could create a Map for each sub property -- keyed by id -- and merge the objects into those maps. Then finally convert those sub-maps back to sub-arrays:
function mergeArray(arr) {
function mergeObject(a, b) {
while (b = b.sub) {
if (!a.sub) a.sub = new Map;
let child = a.sub.get(b.id);
if (child) a = child;
else a.sub.set(b.id, a = { id: b.id, name: b.name });
}
}
function convertMap(map) {
return Array.from(map.values(), obj => {
if (obj.sub) obj.sub = convertMap(obj.sub);
return obj;
});
}
let map = new Map(arr.map(({id, name}) => [id, {id, name}]));
for (let item of arr) mergeObject(map.get(item.id), item);
return convertMap(map);
}
// Demo with input from question
let input = [{"id": 14,"name": "Name14","theme": true,"sub": {"id": 70,"name": "Name70"}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 61,"name": "Name61"}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 4,"name": "Name4","sub": {"id": 5,"name": "Name5","sub": {"id": 29,"name": "Name29"}}}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 4,"name": "Name4","sub": {"id": 5,"name": "Name5","sub": {"id": 8,"name": "Name8","sub": {"id": 163,"name": "Name163"}}}}},{"id": 10,"name": "Name10","sub": {"id": 4,"name": "Name4"}}];
console.log(mergeArray(input));
I would do it with recursive functions, because I think those are more versatile:
const data = [{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 70,
"name": "Name70"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 61,
"name": "Name61"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 29,
"name": "Name29"
}
}
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 8,
"name": "Name8",
"sub": {
"id": 163,
"name": "Name163"
}
}
}
}
},
{
"id": 10,
"name": "Name10",
"sub": {
"id": 4,
"name": "Name4"
}
}
]
// setting up arrays
const recursiveModify = (node) => {
if (typeof node.sub === "undefined") {
return node
} else {
node.sub = [recursiveModify(node.sub)]
return node
}
}
const recursiveReduce = (nodes) => {
return nodes.reduce((a, c) => {
const item = a.find(e => e.id === c.id)
if (!item) {
a.push(c)
} else {
item.sub = recursiveReduce([...item.sub, ...c.sub])
}
return a
}, [])
}
const dataWithArray = data.map(node => {
return recursiveModify(node)
})
const result = recursiveReduce(dataWithArray)
console.log(result)
Unfortunately I could only do it with two passes - one for creating the sub as arrays, then one for actually grouping the data. I'm pretty sure it can be done in one pass - I just have no more time to work it out.
I am trying to simplify the following state:
{
"name": "bulbasaur",
"picture": "https://raw",
"height": 7,
"weight": 69,
"types": [
{
"slot": 1,
"type": {
"name": "poison",
"url": "https://poke"
}
},
{
"slot": 2,
"type": {
"name": "grass",
"url": "https://poke"
}
}
]}
into something like this:
{
"name": "bulbasaur",
"picture": "https://raw",
"height": 7,
"weight": 69,
"types": [ "poison", "grass" ]
}
Also, I would like to mention that I have an array with 151 of these. Not every object contains two types; some only contain one.
I believe that is the reason most of what I have tried so far does not work. Thank you in advance for your help.
Try using map
let obj={ name: "bulbasaur", picture: "https://raw", height: 7, weight: 69, types : [{ slot: 1, type: { name: "poison", url:"https://poke" }}]};
obj.types=obj.types.map( Type => Type.type.name);
console.log(obj.types);
I think this is what you want, you will need to add this snippet in a loop for you dataset and have it pushed into a new array:
const Obj = {
"name": "bulbasaur",
"picture": "https://raw",
"height": 7,
"weight": 69,
"types" : [{
"slot": 1,
"type": {
"name": "poison",
"url":"https://poke"
}}, {
"slot": 2,
"type": {
"name": "grass",
"url":"https://poke"
}}
]};
const newObj = {
...Obj,
types: Obj.types.map((el) => el.type.name),
}
console.log(newObj)
I was able to resolve what I needed by using the logic provided by Ma'moun othman.
"name": "bulbasaur",
"picture": "https://raw",
"height": 7,
"weight": 69,
"types" : [{
"slot": 1,
"type": {
"name": "poison",
"url":"https://poke"
}}, {
"slot": 2,
"type": {
"name": "grass",
"url":"https://poke"
}}
]};
const newObj = {
...Obj,
types: Obj.types.map((el) => el.type.name),
}
console.log(newObj)
I have a JSON dataset which could be very large when it returns, with the following structure for each object:
{
"ctr": 57,
"averageECPC": 23,
"cost": 2732.54,
"margin": 66,
"profit": 2495.9,
"property": {
"value": "Izzby",
"uri": "/Terrago/2"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 2573.13,
"compare": 0
},
"children": [{
"ctr": 79,
"averageECPC": 54,
"cost": 3554.78,
"margin": 88,
"profit": 3145.81,
"property": {
"value": "Comvex",
"uri": "/Octocore/4"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 1247.92,
"compare": 0
}
}]
}
Now I want to search all objects in the array and return only objects which include a string of some sort, but I only want to search certain properties.
I basically have another array which contains the keys I want to search, e.g.
const iteratees = ['ctr', 'property.value', 'status.stage']
I have lodash available within the project, but I have no idea where to start.
Any ideas?
You could use filter(), some() and reduce() to do this.
const iteratees = ['ctr', 'property.value', 'status.stage'];
var searchFor = 'lo';
var result = arr.filter(function(o) {
return iteratees.some(function(e) {
var res = e.split('.').reduce(function(a, b) {
if(a) return a[b];
}, o);
if(res) {
if((res).toString().indexOf(searchFor) != -1) return true;
}
})
})
var arr = [{
"ctr": 'lorem',
"averageECPC": 23,
"cost": 2732.54,
"margin": 66,
"profit": 2495.9,
"property": {
"value": "Izzby",
"uri": "/Terrago/2"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 2573.13,
"compare": 0
},
"children": [{
"ctr": 79,
"averageECPC": 54,
"cost": 3554.78,
"margin": 88,
"profit": 3145.81,
"property": {
"value": "Comvex",
"uri": "/Octocore/4"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 1247.92,
"compare": 0
}
}]
}, {
name: 'lorem',
ctr: 12,
property: {
value: 1
},
status: {
stage: 1
}
}, {
name: 'ipsum'
}]
const iteratees = ['ctr', 'property.value', 'status.stage'];
var searchFor = 'lo';
var result = arr.filter(function(o) {
return iteratees.some(function(e) {
var res = e.split('.').reduce(function(a, b) {
if (a) return a[b];
}, o);
if (res) {
if ((res).toString().indexOf(searchFor) != -1) return true;
}
})
})
console.log(result)