Rename keys in reduce function - javascript

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'];
}

Related

Joining two data sets and calculating different values

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);

How to add object to specific place in JavaScript?

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));
})

how to create DOM element structure in javascript

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);

How to replace an array of objects with an array including newer objects in TypeScript?

For example: I have an array of objects like this:
let arrayOfObjects: [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
Now I want to replace or overwrite the array above with the same array, but including different values (same keys, different values):
let arrayOfObjects: [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
];
How can this be done in TypeScript?
Try this JS
let oneArray = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
let twoArray = [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
];
let newArray = Object.assign([], oneArray, twoArray);
console.log(newArray);
In TS
interface Data {
oneArray: array;
twoArray: array;
}
function merge(data: Data) {
return Object.assign([], data.oneArray, data.twoArray);
}
let user = {
oneArray: [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
],
twoArray: [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
]
};
console.log(merge(user));
Sure :)
let arrayOfObjects = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
const newValues = ['Not Available', 'Not Ready', 'Not Started'];
newValues.forEach((value, i) => {
arrayOfObjects.find(o => o.id === i).name = value;
});
console.log(arrayOfObjects);
I wouldn't recommend this though: functional programming is awesome.
let arrayOfObjects = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
let names = ['Not Available', 'Not Ready', 'Not Started']
let result = arrayOfObjects.map((user, index) => ({ ...user, name: names[index] }))
console.log(result)

Javascript get data totals into list

I have this data:
{
"id": "123",
"name": "name here",
"thsub": {
"637": {
"id": "637",
"name": "Sub 1",
"stats": {
"items": 5,
},
"ons": null
},
"638": {
"id": "638",
"name": "Sub 2",
"stats": {
"items": 10,
},
"ons": null
}
},
"ph": 10,
}
Here's is the code:
mydata = [mydata];
var chList=[];
var thList=[];
var thCount=[];
for (var i = 0; i < mydata.length; i++) {
var obj = mydata[i];
var cl = obj.name;
if (obj.thsub != null) {
chList.push(cl);
}
if(obj.thsub) {
if (i < 10) {
var nme = Object.keys(obj.thsub).map( function(key){
var item = obj.thsub[key];
return item.name;
});
thCount.push(numberofitems);
thList = thList.concat(nme);
thCount = thCount.concat(Array(nme.length).fill(nme.length));
}
}
}
My problem is in the thCount ... what I need to do is to count each "items" on obj.thsub.638 or other ...stats.items and put the totals into thCount like I've got in thList.
So the desired result woule be 5 and 10 in other words: [5, 10] in this case.
thCount would be [5, 10]
How can I do this?
you should access the json objects using key value, index is for arrays. below code just does the thcount for you
var data = {
"id": "123",
"name": "name here",
"thsub": {
"637": {
"id": "637",
"name": "Sub 1",
"stats": {
"items": 5,
},
"ons": null
},
"638": {
"id": "638",
"name": "Sub 2",
"stats": {
"items": 10,
},
"ons": null
}
},
"ph": 10,
};
var thCount = [];
for(key in data.thsub ){
if(data.thsub[key].stats){
thCount.push(data.thsub[key].stats.items);
}
}
console.log(thCount);
Object.values gives you the list of values of a given object and then you can map your result in an array. in ES5:
var arr = Object.values(mydata.thsub).map(function(item) {
return item.stats.items
});
in ES6:
const list = Object.values(mydata.thsub).map(item => item.stats.items);
You could use an iterative recursive approach for getting the wanted values.
function getValues(object, key) {
function iter(o) {
if (key in o) {
result.push(o[key]);
}
Object.keys(o).forEach(function (k) {
if (o[k] && typeof o[k] === 'object') {
iter(o[k]);
}
});
}
var result = [];
iter(object);
return result;
}
var data = { id: "123", name: "name here", thsub: { 637: { id: "637", name: "Sub 1", stats: { items: 5, }, ons: null }, 638: { id: "638", name: "Sub 2", stats: { items: 10, }, ons: null } }, ph: 10, };
console.log(getValues(data, 'items'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources