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);
Related
I am essentially receiving data from another API that takes the following structure
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
What I was doing what creating a new array which essentially gets each unique type and then does a total count and an individual count of each unique name.
I also have another data set I combine with this but omitted for this question.
The working code I have is
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
const dataReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
'email.success': 0,
'email.failed': 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;
}, {});
console.log(dataReduced);
What I can't figure out however, because it is matching on email.success is how to rename these in my final output. I essentially want to remove the email. part.
So instead, the console.log should be
{
Type 1: [{
count: 2,
failed: 1,
success: 1
}],
Type 2: [{
count: 1,
failed: 0,
success: 1
}],
Type 3: [{
count: 1,
failed: 0,
success: 1
}]
}
How would I achieve this?
Thanks
You can do something like this
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
const dataReduced = ob.dataOne.reduce((acc, o) => {
const name = o.name.replace('email.', '')
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
'success': 0,
'failed': 0,
},
];
}
acc[o.type][0].count = (acc[o.type][0].count || 0) + 1;
acc[o.type][0][name] = acc[o.type][0][name] + 1;
return acc;
}, {});
console.log(dataReduced);
I've no clue why the console output is actually in your Browser console and not in this JS Constainer, but here, no reduce, but I don't even see the reason why a reduce would be better here:
var arr = [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
];
var result = [];
for (var o of arr) {
if (!result.hasOwnProperty(o.type)) {
var newObj = {
count: 1,
failed: 0,
success: 0,
};
if (o.name.indexOf('failed') !== -1) {
newObj.failed++;
}
if (o.name.indexOf('success') !== -1) {
newObj.success++;
}
result[o.type] = [newObj];
} else {
result[o.type][0].count++;
if (o.name.indexOf('failed') !== -1) {
result[o.type][0].failed++;
}
if (o.name.indexOf('success') !== -1) {
result[o.type][0].success++;
}
}
}
console.log(result);
I resolved this using the for in loop:
for(let elem in dataReduced){
dataReduced[elem][0]['success'] = dataReduced[elem][0]['email.success'];
dataReduced[elem][0]['failed'] = dataReduced[elem][0]['email.failed'];
delete dataReduced[elem][0]['email.success'];
delete dataReduced[elem][0]['email.failed'];
}
I am using a library React simple chatbot and add data from database, but the problem is i have array like below
Array from Database
[{
"id": 1,
"message": "What is your name?",
"type": "text",
"trigger": "2"
}, {
"id": 2,
"message": "What is your age?",
"type": "text",
"trigger": "3"
}, {
"id": 3,
"message": "What is your date of birth?",
"type": "text",
"trigger": "1"
}]
But i need something like below
[{
"id": 1,
"message": "What is your name?",
"type": "text",
"trigger": "2"
}, {
"id": 2,
"user": true,
"trigger": 3
}, {
"id": 3,
"message": "What is your age?",
"type": "text",
"trigger": "4"
}, {
"id": 4,
"user": true,
"trigger": 5
}, {
"id": 5,
"message": "What is your date of birth?",
"type": "text",
"trigger": "6"
}, {
"id": 6,
"user": true,
"trigger": 1
}]
Please help me to create array like this. I write some code, but doesn't work for me. My code print duplicate ids. Basically i want to pass object after every object with increment id.
Code
fetch("api_url_here")
.then((res) => {
return res.json();
})
.then((data) => {
const steps = data.map((res, index) => {
return {
id: index,
message: res.question,
type: res.type,
trigger: res.trigger,
};
});
var array = [];
var trigger = 1;
steps.forEach((step, index) => {
index = index;
array.push(step);
if (step.type == "text") {
array.push({
id: trigger + 1,
user: true,
trigger: trigger,
});
}
trigger++;
});
document.getElementById("json").append(JSON.stringify(array));
})
Current Output
[{
"id": 0,
"message": "What is your name?",
"type": "text",
"trigger": "2"
}, {
"id": 2,
"user": true,
"trigger": 1
}, {
"id": 1,
"message": "What is your age?",
"type": "text",
"trigger": "3"
}, {
"id": 3,
"user": true,
"trigger": 2
}, {
"id": 2,
"message": "What is your date of birth?",
"type": "text",
"trigger": "1"
}, {
"id": 4,
"user": true,
"trigger": 3
}]
Any solution appreciated!
You only need to add this as a final step to correct the id and trigger
const almost = [{
"id": 0,
"message": "What is your name?",
"type": "text",
"trigger": "2"
}, {
"id": 2,
"user": true,
"trigger": 1
}, {
"id": 1,
"message": "What is your age?",
"type": "text",
"trigger": "3"
}, {
"id": 3,
"user": true,
"trigger": 2
}, {
"id": 2,
"message": "What is your date of birth?",
"type": "text",
"trigger": "1"
}, {
"id": 4,
"user": true,
"trigger": 3
}]
const final = almost.map( (obj,index) => {
const newObj = {};
Object.assign(newObj,
obj,
{
"id":index+1,
"trigger": index+2
}
);
return newObj;
});
final[final.length-1].trigger = 1;
console.log(final)
EDIT: to make it clear, your code would look like:
fetch("api_url_here")
.then((res) => {
return res.json();
})
.then((data) => {
const steps = data.map((res, index) => {
return {
id: index,
message: res.question,
type: res.type,
trigger: res.trigger,
};
});
var array = [];
var trigger = 1;
steps.forEach((step, index) => {
index = index;
array.push(step);
if (step.type == "text") {
array.push({
id: trigger + 1,
user: true,
trigger: trigger,
});
}
trigger++;
});
array = array.map( (obj,index) => {
const newObj = {};
Object.assign(newObj,
obj,
{
"id":index+1,
"trigger": index+2
}
);
return newObj;
});
array[array.length-1].trigger = 1;
document.getElementById("json").append(JSON.stringify(array));
})
I've got this code and I need to transform ti to DOM structure (more information below)
const data = [
{
"type": "paragraph",
"children": [
{
"type": "text",
"text": "Hey all!"
},
{
"type": "break"
},
{
"type": "break"
},
{
"type": "text",
"text": " It's been a while since we partied "
},
{
"type": "important",
"children": [
{
"type": "text",
"text": "together"
}
]
},
{
"type": "text",
"text": " in a pool full of people!"
}
]
},
{
"type": "heading",
"id": "table-of-contents",
"level" : 2,
"children": [
{
"type": "text",
"text": "Table of contents:"
}
]
},
{
"type": "list",
"bullet": "decimal",
"children": [
{
"type": "listitem",
"children": [
{
"type": "anchor",
"href": "#table-of-contents",
"children": [
{
"type": "text",
"text": "How to start a podcast?"
},
{
"type": "text",
"text": ""
}
]
},
{
"type": "text",
"text": "Where to find your topics?"
}
]
},
{
"type": "listitem",
"children": [
{
"type": "text",
"text": "Where to find your topics?"
}
]
},
{
"type": "listitem",
"children": [
{
"type": "text",
"text": "What equipment do you need?"
}
]
}
]
}
]
What is the best way to do it?
I mean, should I do
const wrapper = document.createElement("div");
data.forEach(element => {
if(element.type === "paragraph") {
const paragraph = document.createElement("p");
element.children.forEach(kiddo => {
if(kiddo.type === "text") {
const textNode = document.createTextNode(kiddo.text);
paragraph.appendChild(textNode);
}
});
}
})
..and so on? I mean do I have to use "createElement/createTextNode" functions or does javascript have some kind of DOMBuilder than I can convert such structure into DOM?
As Teemu says, you can create your own "DOM Builder" by adding methods to an object and recursing.
const body = document.getElementsByTagName("body")[0];
const wrapper = document.createElement("div");
const DOMBuilder = {
"anchor" : e => {
var a = document.createElement("a");
a.href = e.href;
return a;
},
"heading" : e => { return document.createElement("h" + e.level); },
"list" : e => {
return document.createElement((e.bullet == "decimal") ? "ol" : "ul");
},
"listitem" : () => { return document.createElement("li"); },
"paragraph" : () => {return document.createElement("p"); },
"text" : e => {return document.createTextNode(e.text); },
}
function CreateDOMElement(e) {
var ne;
if (ne = DOMBuilder[e.type]?.(e)) {
if (e.id) ne.id = e.id;
e.children?.forEach(c => {
var ce = CreateDOMElement(c); if (ce) ne.appendChild(ce);
});
return ne;
}
}
data.forEach(element => {
var ne = CreateDOMElement(element); if (ne) wrapper.appendChild(ne);
});
body.appendChild(wrapper);
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>
I have an array like this :
[
{
"title": "name",
"value": ""
},
{
"title": "version",
"value": ""
},
{
"title": "inventory_name",
"value": ""
},
{
"title": "inventory_version",
"value": ""
},
{
"title": "differed",
"value": ""
},
{
"title": "differed_name",
"value": ""
},
{
"title": "accept_error_while_reboot",
"value": ""
},
{
"title": "setup_check",
"value": ""
},
{
"title": "setup_install",
"value": ""
},
{
"title": "setup_install_partial",
"value": ""
},
{
"title": "params_install",
"value": ""
},
{
"title": "params_install_partial",
"value": ""
},
{
"title": "results_install_ok",
"value": ""
},
{
"title": "results_install_reboot_defered",
"value": ""
},
{
"title": "results_install_reboot_immediate",
"value": ""
},
{
"title": "results_install_partial_ok",
"value": ""
},
{
"title": "results_install_partial_reboot_defered",
"value": ""
},
{
"title": "results_install_partial_reboot_immediate",
"value": ""
}
];
Is it possible to make subarrays that contains the same title field string ?
For example in this case , I will have :
array1 = [
{
"title": "differed",
"value": ""
},
{
"title": "differed_name",
"value": ""
}
]
array2 = [
{
"title": "setup_check",
"value": ""
},
{
"title": "setup_install",
"value": ""
},
{
"title": "setup_install_partial",
"value": ""
}
]
and so on...
In case of single elements , I should have :
[
{
"title": "name",
"value": ""
}
]
I'm searching for a generic approach.
I know I can use, for example, indexOf('results') with filter function, however I'd like if it's possible to avoid the hardcode since it's not always the same titles.
Any ideas ?
Fiddle
You can use an object to group similar items:
var groups = {};
parameter_list.forEach(function(p){
var key = p.title.split('_')[0];
if(!groups[key]) {
groups[key] = [];
}
groups[key].push(p);
});
Working demo:
http://jsfiddle.net/t459o6v1/3/
Group the data with .reduce()
var groups = data.reduce(function(result, currentValue) {
var key = currentValue.title.split("_")[0];
if (typeof result[key] === "undefined") {
result[key] = [];
}
result[key].push(currentValue);
return result;
}, {});
And then (if needed) use .map() to transform the object into "subarrays"
var subArrays = Object.keys(groups).map(function(key) {
return groups[key];
});
var data = [{
"title": "name",
"value": ""
}, {
"title": "version",
"value": ""
}, {
"title": "inventory_name",
"value": ""
}, {
"title": "inventory_version",
"value": ""
}, {
"title": "differed",
"value": ""
}, {
"title": "differed_name",
"value": ""
}, {
"title": "accept_error_while_reboot",
"value": ""
}, {
"title": "setup_check",
"value": ""
}, {
"title": "setup_install",
"value": ""
}, {
"title": "setup_install_partial",
"value": ""
}, {
"title": "params_install",
"value": ""
}, {
"title": "params_install_partial",
"value": ""
}, {
"title": "results_install_ok",
"value": ""
}, {
"title": "results_install_reboot_defered",
"value": ""
}, {
"title": "results_install_reboot_immediate",
"value": ""
}, {
"title": "results_install_partial_ok",
"value": ""
}, {
"title": "results_install_partial_reboot_defered",
"value": ""
}, {
"title": "results_install_partial_reboot_immediate",
"value": ""
}];
var groups = data.reduce(function(result, currentValue) {
var key = currentValue.title.split("_")[0];
if (typeof result[key] === "undefined") {
result[key] = [];
}
result[key].push(currentValue);
return result;
}, {});
var subArrays = Object.keys(groups).map(function(key) {
return groups[key];
});
console.log(JSON.stringify(subArrays));
I came up with a solution using Immutable.JS, but you could probably do something similar with lodash or underscore. Note that this is a functional version, not imperative.
First create a function that gets the prefix:
function getPrefix(name) {
var substr = name.substring(0, name.indexOf('_'))
return substr ? substr : name;
}
Then use the groupBy function:
Immutable.fromJS(arr).groupBy(element => getPrefix( element['title']))
.toJS();
This will give you an array of arrays with the title as it's key.