Given an array like this, how would I get a count of all charts in a particular category. Each category can have multiple or no groups.
{
"categories":[
{
"title":"category 1",
"id":"cat1",
"groups":[
{
"title":"group 1",
"id":"grp1",
"charts":[
{
"title":"chart 1",
"id":"chart1",
"type":"line"
}
]
}
]
},
{
"title":"category 2",
"id":"cat2",
"charts":[
{
"title":"chart 2",
"id":"chart2",
"type":"line"
}
]
},
{
"title":"category 3",
"id":"cat3",
"charts":[
{
"title":"chart 3",
"id":"chart3",
"type":"line"
}
]
}
]
}
Is a one-liner okay?
Assuming data is your JSON structure:
data.categories
.map(c => [
c.title,
c.groups ?
c.groups.map(g => g.charts.length).reduce((a, b) => a+b) :
c.charts.length
])
var object = {
"categories": [{
"title": "category 1",
"id": "cat1",
"groups": [{
"title": "group 1",
"id": "grp1",
"charts": [{
"title": "chart 1",
"id": "chart1",
"type": "line"
}]
}]
}, {
"title": "category 2",
"id": "cat2",
"charts": [{
"title": "chart 2",
"id": "chart2",
"type": "line"
}]
}, {
"title": "category 3",
"id": "cat3",
"charts": [{
"title": "chart 3",
"id": "chart3",
"type": "line"
}]
}]
}
var groupPerCategories = [];
object.categories.forEach(function(category) {
var tot = 0;
if (category.groups != undefined) {
category.groups.forEach(function(group) {
if(group.charts != undefined){
tot += group.charts.length;
}
});
}
if (category.charts != undefined) {
tot += category.charts.length;
}
console.log(tot);
});
You could count the properties with default.
var data = { "categories": [{ "title": "category 1", "id": "cat1", "groups": [{ "title": "group 1", "id": "grp1", "charts": [{ "title": "chart 1", "id": "chart1", "type": "line" }] }] }, { "title": "category 2", "id": "cat2", "charts": [{ "title": "chart 2", "id": "chart2", "type": "line" }] }, { "title": "category 3", "id": "cat3", "charts": [{ "title": "chart 3", "id": "chart3", "type": "line" }] }] },
count = {};
data.categories.forEach(function (a) {
var countCharts = function (r, a) {
return r + (a.charts || []).length;
};
count[a.title] = (count[a.title] || 0) +
(a.groups ||[]).reduce(countCharts, 0) +
countCharts(0, a);
});
console.log(count);
Hope this will help you :)
var categories = [
{
"title":"category 1",
"id":"cat1",
"groups":[
{
"title":"group 1",
"id":"grp1",
"charts":[
{
"title":"chart 1",
"id":"chart1",
"type":"line"
}
]
}
]
},
{
"title":"category 2",
"id":"cat2",
"charts":[
{
"title":"chart 2",
"id":"chart2",
"type":"line"
}
]
},
{
"title":"category 3",
"id":"cat3",
"charts":[
{
"title":"chart 3",
"id":"chart3",
"type":"line"
},
{
"title":"chart 3",
"id":"chart3",
"type":"line"
},
{
"title":"chart 3",
"id":"chart3",
"type":"line"
}
]
},
{
"title":"category 4",
"id":"cat4",
"groups":[
{
"title":"group 4",
"id":"grp4",
"charts":[
{
"title":"chart 1",
"id":"chart1",
"type":"line"
},
{
"title":"chart 1",
"id":"chart1",
"type":"line"
}
]
}
]
}
];
function countCategoryItems(data, category) {
if(!data ) return 0
if(!category) return 0
var cat = _.filter(data, function(obj){ return obj.title == category; });
if(!cat.length) return 0
var groups = cat[0].groups || []
if(!groups.length) return cat[0].charts.length
var count = 0
for (var i=0;i<groups.length;i++) {
count += groups[i].charts.length
}
return count
}
$(function() {
console.log(countCategoryItems(categories, 'category 1'))
console.log(countCategoryItems(categories, 'category 2'))
console.log(countCategoryItems(categories, 'category 3'))
console.log(countCategoryItems(categories, 'category 4'))
})
<script src="http://underscorejs.org/underscore.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Related
So I have an Object of two arrays that I was trying to combine.
What I am essentially aiming to do is get the unique types from dataOne. I then need the total count of occurrences for each type. Then, I need to know for each type the total count of success and failed.
Then from dataTwo I need to include the number of clicks for each type.
At the moment I have the following which achieves this, although I am sure it can be tidied up.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
console.log(result);
What I am now trying to do is insert the success rate as a percentage. So I need the output to be like so
{
"Type 1": [
{
"count": 2,
"success": 1,
"failed": 1,
"click": 2,
"successPecentage": 50
}
],
"Type 2": [
{
"count": 1,
"success": 1,
"failed": 0,
"click": 2,
"successPecentage": 100
}
],
"Type 3": [
{
"count": 1,
"success": 1,
"failed": 0,
"click": 1,
"successPecentage": 100,
}
]
}
How would I achieve this?
Thanks
Option 1
You can loop over your object one more time and insert the percentage. It is not the best solution performance-wise, but it's simpler.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
for (const type of Object.keys(result)) {
const obj = result[type][0];
obj.successPercentage = obj.success / obj.count * 100;
}
console.log(result);
Option 2
You can insert the calculation directly into .reduce(), and it will work fine because only the value will be overwritten with each iteration, and only the last, correct value will be in the output.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
acc[o.type][0].successPercentage = acc[o.type][0].success / acc[o.type][0].count * 100;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
console.log(result);
The desired objective may be achieved by adding one line:
acc[o.type][0].successPercentage = Math.round(acc[o.type][0].success / acc[o.type][0].count * 100); as shown in below snippet
Code Snippet
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
acc[o.type][0].successPercentage = Math.round(acc[o.type][0].success / acc[o.type][0].count * 100);
return acc;
}, dataOneReduced);
console.log(result);
Take the following json structure, how can I filter it on the categories node that is 4 levels down.
For example, only return a json object which contains items that have "cat8" in this example, i'd get Items A and B?
I'm using node and have lodash installed.
I would filter the data at source normally, but the dataset I have, I can't do this.
{
"items": [
{
"fields": {
"title": "Item A",
"section1": {
"fields": {
"title": "Level 2 title",
"section2": {
"fields": {
"title": "Level 3 title",
"section3": {
"fields": {
"title": "Level 4 title",
"categories": [
"cat1",
"cat6",
"cat8"
]
}
}
}
}
}
}
}
},
{
"fields": {
"title": "Item B",
"section1": {
"fields": {
"title": "Level 2 title",
"section2": {
"fields": {
"title": "Level 3 title",
"section3": {
"fields": {
"title": "Level 4 title",
"categories": [
"cat1",
"cat8"
]
}
}
}
}
}
}
}
},
{
"fields": {
"title": "Item C",
"section1": {
"fields": {
"title": "Level 2 title",
"section2": {
"fields": {
"title": "Level 3 title",
"section3": {
"fields": {
"title": "Level 4 title",
"categories": [
"cat1",
"cat3"
]
}
}
}
}
}
}
}
}
]
}
with lodash its pretty simple
const results = _.filter(deepObject,['path.to.deep.property', expectedValue])
Without knowing the structure in advance, you could visit all objects and check if the values contains the wanted value.
const
hasCategory = o => Object
.values(o)
.some(p => p === category || p && typeof p === 'object' && hasCategory(p)),
data = { items: [{ fields: { title: "Item A", section1: { fields: { title: "Level 2 title", section2: { fields: { title: "Level 3 title", section3: { fields: { title: "Level 4 title", categories: ["cat1", "cat6", "cat8"] } } } } } } } }, { fields: { title: "Item B", section1: { fields: { title: "Level 2 title", section2: { fields: { title: "Level 3 title", section3: { fields: { title: "Level 4 title", categories: ["cat1", "cat8"] } } } } } } } }, { fields: { title: "Item C", section1: { fields: { title: "Level 2 title", section2: { fields: { title: "Level 3 title", section3: { fields: { title: "Level 4 title", categories: ["cat1", "cat3"] } } } } } } } }] },
category = 'cat8',
result = data.items.filter(hasCategory);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
How can you merge this JSON data. Simply merging the same objects and adding the different "product_code" into an array from the same JSON element dependent on the "product_web_name".
{
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": "Bb111",
"product_description": "test desc b",
"product_web_name": "Product B",
},
{
"product_id": "2",
"product_code": "Bb222",
"product_description": "test desc b",
"product_web_name": "Product B"
},
{
"product_id": "2",
"product_code": "Bb333",
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
}
The Final output results or the outcome would be something like this
{
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": [
{
"product_code_id":"1",
"product_code": "Bb111"
},
{
"product_code_id":"2",
"product_code": "Bb222"
},
{
"product_code_id":"3",
"product_code": "Bb333"
}
],
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
}
I want to merge the JSON data with adding the same element. I have tried the code so far.
var mergethis = {};
data = result.search_result.results.filter(function(entry) {
var previous;
if(mergethis.hasOwnProperty(entry.product_web_name)) {
previous = mergethis[entry.product_web_name];
previous.data.push(entry.data);
return false;
}
if (!Array.isArray(entry.data)) {
entry.data = [entry.data];
}
mergethis[entry.product_web_name] = entry;
return true;
});
console.log(mergethis);
Here's a snippet below to get you started, you can massage the output as needed. All of this came from using Array.prototype.reduce Array.prototype.map and Object.prototype.keys. A utility tool may also help simplify if you look at lodash or underscore
var data = {
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": "Bb111",
"product_description": "test desc b",
"product_web_name": "Product B",
},
{
"product_id": "2",
"product_code": "Bb222",
"product_description": "test desc b",
"product_web_name": "Product B"
},
{
"product_id": "2",
"product_code": "Bb333",
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
};
var grouped = data.search_result.results.reduce(function(acc, value) {
var groupArray = acc[value.product_id];
if (!groupArray) {
groupArray = [];
acc[value.product_id] = groupArray;
}
groupArray.push(value);
return acc;
}, {});
//console.log(grouped);
data.search_result.results = Object.keys(grouped).map(function(key) {
//return grouped[key][0];
return {
"product_id": grouped[key][0].product_id,
"product_description": grouped[key][0].product_description,
"product_code": grouped[key].length === 1 ? grouped[key][0].product_code : grouped[key]
};
});
console.log(data);
for(var i = 0 ; i < a.search_result.results.length; i++){
switch(a.search_result.results[i].product_web_name){
case 'Product A':
a.search_result.results[i].product_code = 'something1';
break;
case 'Product B':
a.search_result.results[i].product_code = 'something2';
break;
default :
a.search_result.results[i].product_code = 'something3';
}
}
I have some data and I need to filter out the data thats null or empty and create a new data list thats filtered.
In this case sometimes "names" array is null so I need that data out.
{
"people": [
{
"id": "2",
"description": "desc here",
"names": [
{
"name": "name here",
},
{
"name": "name here",
}
],
"other": "0"
},
{
"id": "200",
"description": "desc here",
"names": null
"other": "0"
},
{
"id": "64",
"description": "desc here",
"names": [
{
"name": "name here",
},
{
"name": "name here",
}
],
"other": "1"
}
]
}
How can I do this?
You could iterate the arrays and objects recursive until a primitive is found. Then check and return the value.
function copy(object) {
var o;
if (Array.isArray(object)) {
return object.reduce(function (r, a) {
var v = copy(a);
v.names !== null && v.names !== '' && r.push(v);
return r;
}, []);
}
if (object !== null && typeof object === 'object') {
o = {};
Object.keys(object).forEach(function (k) {
o[k] = copy(object[k]);
});
return o;
}
return object;
}
var data = { people: [{ id: "2", description: "desc here", names: [{ id: "345", name: "name here", }, { id: "54", name: "name here", foo: "", }], other: "0" }, { id: "2", description: "desc here", names: null, other: "0" }, { id: "64", description: "desc here", names: [{ id: "87", name: "name here", }, { id: "53", name: "name here", }], other: "1" }] },
result = copy(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var newArray = oldArray.filter(function(v){return v!==''});
new_array=yourObject.people.filter(function(elem){
return elem.names!==null && elem.names!==""
});
Hi Here is an array of object receiving from the API response and i'm using angular.js and javascript and i want get object with find by id and id is "1_1_1".is this possible? i'm stuck on it. please help.
[
{
"text": "folder 1",
"parent": {
"id": "1",
"name": "folder 1"
},
"children": [
{
"text": "folder 1_1",
"parent": {
"id": "1_1",
"name": "folder 1_1"
},
"children": [
{
"text": "folder 1_1_1",
"parent": {
"id": "1_1_1",
"name": "folder 1_1_1"
},
"children": []
},
{
"text": "folder 1_1_2",
"parent": {
"id": "1_1_2",
"name": "folder 1_1_2"
},
"children": []
}
]
},
{
"text": "folder 1_2",
"parent": {
"id": "1_2",
"name": "folder 1_2"
},
"children": [
{
"text": "folder 1_2_1",
"parent": {
"id": "1_2_1",
"name": "folder 1_2_1"
},
"children": []
}
]
}
]
},
{
"text": "folder 2",
"parent": {
"id": "2",
"name": "folder 2"
},
"children": [
{
"text" : "folder 2_1",
"parent": {
"id": "2_1",
"name": "folder 2_1"
},
"children": []
},
{
"text" : "folder 2_2",
"parent": {
"id": "2_2",
"name": "folder 2_2"
},
"children": []
},
{
"text" : "folder 2_3",
"parent": {
"id": "2_3",
"name": "folder 2_3"
},
"children": [
{
"text" : "folder 2_3_1",
"parent": {
"id": "2_3_1",
"name": "folder 2_3_1"
},
"children": []
}
]
},
]
},
{
"text": "folder 3",
"parent": {
"id": "3",
"name": "folder 3"
},
"children": []
}
]
Try this:
function isMatch(element, criteria) {
var parent = null;
if (typeof element.parent !== 'undefined') {
parent = element.parent;
if (typeof parent.id !== 'undefined') {
return (parent.id === criteria);
}
}
return false;
}
function findById (tree, criteria) {
for (var i = 0; i < tree.length; i = i + 1) {
var obj = tree[i];
if (isMatch(obj, criteria)) {
return obj;
};
if (typeof obj.children !== 'undefined') {
var foundElement = findById(obj.children, criteria);
if (foundElement != null) {
return foundElement;
}
}
}
return null;
}
There is no built in function in either JavaScript or Angular to do that.
However this is a problem that can be solved with recursion quite easily:
function findById(id, items) {
for (var i = 0; i < items.length; i++) {
if (items[i].id === id) {
return items[i];
}
var result = findById(id, items[i].children);
if (result !== null) {
return result;
}
}
return null;
}