Add New Key Value Pair - javascript

How can I manipulate my source array using javascript in order to get the following:
[ {Metric: totalRevenue, 2019-12-28: 91722000000, 2019-09-30: 63936000000}, {Metric: costOfRevenue, 2019-12-28: 56773000000, 2019-09-30: 39771000000}, ...etc. ]
Where Each financial statement item is considered a new metric that has a new key value that combines fiscalDate value and the value for that Line Item.
Example JSON here:
iex = [
{
reportDate: "2019-12-31",
fiscalDate: "2019-12-28",
currency: "USD",
totalRevenue: 91722000000,
costOfRevenue: 56773000000,
grossProfit: 34949000000,
researchAndDevelopment: 4451000000,
sellingGeneralAndAdmin: 5197000000,
operatingExpense: 66421000000,
operatingIncome: 25301000000,
otherIncomeExpenseNet: 617000000,
ebit: 25301000000,
interestIncome: 785000000,
pretaxIncome: 25918000000,
incomeTax: 3682000000,
minorityInterest: 0,
netIncome: 22236000000,
netIncomeBasic: 22236000000,
},
{
"reportDate": "2019-09-30",
"fiscalDate": "2019-09-28",
"currency": "USD",
"totalRevenue": 63936000000,
"costOfRevenue": 39771000000,
"grossProfit": 24165000000,
"researchAndDevelopment": 4110000000,
"sellingGeneralAndAdmin": 4578000000,
"operatingExpense": 48459000000,
"operatingIncome": 15477000000,
"otherIncomeExpenseNet": 650000000,
"ebit": 15477000000,
"interestIncome": 810000000,
"pretaxIncome": 16127000000,
"incomeTax": 2441000000,
"minorityInterest": 0,
"netIncome": 13686000000,
"netIncomeBasic": 13686000000
}
];
Image of Data Source: IEX data array
Code I've tried below:
function generateChartData(iex) {
var chartData = [],
categories = {};
for (var i = 0; i < iex.length; i++) {
var totalRevenue = iex[i].totalRevenue;
var fiscalDate = iex[i].fiscalDate;
// add new data point
if (categories[fiscalDate] === undefined) {
categories[fiscalDate] = {
Metric: totalRevenue,
};
chartData.push(categories[fiscalDate]);
}
// add value to existing data point
categories[fiscalDate][fiscalDate] = totalRevenue;
}
return chartData;
}

I have sorted the method, now can be done one loop.
function generateChartData(iex) {
return iex.reduce(
(m, item) => {
const fiscalDate = item["fiscalDate"];
if (!m[0][fiscalDate]) m[0][fiscalDate] = 0;
if (!m[1][fiscalDate]) m[1][fiscalDate] = 0;
if (!m[2][fiscalDate]) m[2][fiscalDate] = 0;
m[0][fiscalDate] += item.totalRevenue;
m[1][fiscalDate] += item.costOfRevenue;
m[2][fiscalDate] += item.grossProfit;
return m;
},
[
{ Metric: "totalRevenue" },
{ Metric: "costOfRevenue" },
{ Metric: "grossProfit" },
]
);
}
const iex = [{"reportDate":"2019-12-31","fiscalDate":"2019-12-28","currency":"USD","totalRevenue":91722000000,"costOfRevenue":56773000000,"grossProfit":34949000000,"researchAndDevelopment":4451000000,"sellingGeneralAndAdmin":5197000000,"operatingExpense":66421000000,"operatingIncome":25301000000,"otherIncomeExpenseNet":617000000,"ebit":25301000000,"interestIncome":785000000,"pretaxIncome":25918000000,"incomeTax":3682000000,"minorityInterest":0,"netIncome":22236000000,"netIncomeBasic":22236000000},{"reportDate":"2019-09-30","fiscalDate":"2019-09-28","currency":"USD","totalRevenue":63936000000,"costOfRevenue":39771000000,"grossProfit":24165000000,"researchAndDevelopment":4110000000,"sellingGeneralAndAdmin":4578000000,"operatingExpense":48459000000,"operatingIncome":15477000000,"otherIncomeExpenseNet":650000000,"ebit":15477000000,"interestIncome":810000000,"pretaxIncome":16127000000,"incomeTax":2441000000,"minorityInterest":0,"netIncome":13686000000,"netIncomeBasic":13686000000}]
console.log(
generateChartData(iex)
);
Another way:
function generateChartData(keys, iex) {
const [k1, k2, k3] = keys;
const def = keys.map((k) => ({ Metric: k }));
return iex.reduce((m, item) => {
const fiscalDate = item.fiscalDate;
if (!m[0][fiscalDate]) m[0][fiscalDate] = 0;
if (!m[1][fiscalDate]) m[1][fiscalDate] = 0;
if (!m[2][fiscalDate]) m[2][fiscalDate] = 0;
m[0][fiscalDate] += item[k1];
m[1][fiscalDate] += item[k2];
m[2][fiscalDate] += item[k3];
return m;
}, def);
}
const iex = [{"reportDate":"2019-12-31","fiscalDate":"2019-12-28","currency":"USD","totalRevenue":91722000000,"costOfRevenue":56773000000,"grossProfit":34949000000,"researchAndDevelopment":4451000000,"sellingGeneralAndAdmin":5197000000,"operatingExpense":66421000000,"operatingIncome":25301000000,"otherIncomeExpenseNet":617000000,"ebit":25301000000,"interestIncome":785000000,"pretaxIncome":25918000000,"incomeTax":3682000000,"minorityInterest":0,"netIncome":22236000000,"netIncomeBasic":22236000000},{"reportDate":"2019-09-30","fiscalDate":"2019-09-28","currency":"USD","totalRevenue":63936000000,"costOfRevenue":39771000000,"grossProfit":24165000000,"researchAndDevelopment":4110000000,"sellingGeneralAndAdmin":4578000000,"operatingExpense":48459000000,"operatingIncome":15477000000,"otherIncomeExpenseNet":650000000,"ebit":15477000000,"interestIncome":810000000,"pretaxIncome":16127000000,"incomeTax":2441000000,"minorityInterest":0,"netIncome":13686000000,"netIncomeBasic":13686000000}]
console.log(
generateChartData(["totalRevenue", "costOfRevenue", "grossProfit"], iex)
);

Related

Generated a Nested Object from TLV

I am trying to parse a TLV string and need to generate a nested object from that string.
const text = 'AA06ClaireCC04JackBB03TomEE05James'
The output needs to look like this:
"Acrobatic Artist": {
"AA": {
"Claire": {
"Curious Camper": {
"CC": {
"Jack": {
"Baddest Banana": {
"BB": {
"Tom": {
"Energetic Elephant": {
"EE": {
"James" : "LASTRECORD"
}
}
}
}
}
}
}
}
}
}
}
Here is what I currently have:
const map = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
}
function createJson(str) {
let json = {}
let remainingText = str
while(remainingText.length > 0) {
const tag = remainingText.substring(0, 2)
const len = remainingText.substring(2, 4)
const val = remainingText.substring(4, len)
const offset = tag.length + len.length + parseInt(len, 16)
remainingText = remainingText.substring(offset)
console.log('new text: ' + remainingText)
json[map[tag]] = {}
json[map[tag]][tag] = {}
json[map[tag]][tag][val] = {}
}
return json
}
But this just creates an object that looks like this:
{
Acrobatic Artist: {
AA: {
Claire: {}
}
},
Baddest Banana: {
BB: {
Tom: {}
}
},
Curious Camper: {
CC: {
Jack: {}
}
},
Energetic Elephant: {
EE: {
James: {}
}
}
}
Here is my fiddle:
https://jsfiddle.net/kzaiwo/y9m2h60t/8/
Note:
Please disregard the LASTRECORD part. I just added that to complete the key-value pair (for the last pair) in the above example. Thank you!
Thanks!
If you keep a reference to a prev value, which starts off as the original json object, you can then continuously update it and its children. When you're updating your object within the while loop you can update prev, and set it to the last child object that you create so that on the next iteration of your loop that particular child object will be updated to contain the new key-value pairs.
const map = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
};
const text = 'AA06ClaireCC04JackBB03TomEE05James';
function createJson(str) {
let json = {};
let prev = json;
let remainingText = str;
while (remainingText.length > 0) {
const tag = remainingText.substring(0, 2);
const len = remainingText.substring(2, 4);
const val = remainingText.substring(4, 4 + parseInt(len, 16));
const offset = tag.length + len.length + parseInt(len, 16);
remainingText = remainingText.substring(offset);
prev[map[tag]] = {};
prev[map[tag]][tag] = {};
prev = prev[map[tag]][tag][val] = {};
}
return json;
}
console.log(createJson(text));
Given the regular structure of your string (2-character code + 2-character number + characters), you can use a simple regex to split out the various parts.
From there you can (flat) map each section into an array of keys.
Finally, you can reduce-right the array to produce the result you want.
const map = {AA:"Acrobatic Artist",BB:"Baddest Banana",CC:"Curious Camper",DD:"Desperate Driver",EE:"Energetic Elephant"};
const text = "AA06ClaireCC04JackBB03TomEE05James";
// Parses a code, length and value from the start of the provided string
const parseSection = (str) => {
const [, code, valueLength] = str.match(/^(\w{2})([a-fA-F0-9]{2})/);
const length = parseInt(valueLength, 16) + 4;
return {
code,
length,
type: map[code],
value: str.slice(4, length),
};
};
// Iterates through the string, extracting sections until finished
const parseTlv = (str) => {
const sections = [];
while (str.length) {
const section = parseSection(str);
sections.push(section);
str = str.slice(section.length);
}
return sections;
};
// Map each section to a flat array of keys then reduce-right to form
// a tree structure
const lastRecord = {};
const result = parseTlv(text)
.flatMap(({ type, code, value }) => [type, code, value])
.reduceRight(
(obj, key) => ({
[key]: obj,
}),
lastRecord
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; }
Here is a two part solution:
for() loop: create an array of items based on <tag><len><val> patterns
.reduce(): build the nested object from the items array
const input = 'AA06ClaireCC04JackBB03TomEE05James';
const tagMap = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
};
let items = [];
for(let i = 0; i < input.length; ) {
let tag = input.substring(i, i + 2);
let len = parseInt(input.substring(i + 2, i + 4), 16);
let val = input.substring(i + 4, i + 4 + len);
items.push([tag, val]);
i += 4 + len;
}
let result = {};
items.reduce((obj, arr) => {
const tag = arr[0];
const val = arr[1];
const name = tagMap[tag] || 'unknown';
//console.log(name, tag, val);
[name, tag, val].forEach(key => {
obj[key] = {};
obj = obj[key];
});
return obj;
}, result);
console.log(result);
Output:
{
"Acrobatic Artist": {
"AA": {
"Claire": {
"Curious Camper": {
"CC": {
"Jack": {
"Baddest Banana": {
"BB": {
"Tom": {
"Energetic Elephant": {
"EE": {
"James": {}
}
}
}
}
}
}
}
}
}
}
}
}
Note: the resulting object has an empty {} as the innermost value; you could replace that with "LASTRECORD" if needed

remove 2 common array item javascript

I have 2 arrays
let arryOne =
[
{
'catRefId' : "200531-2"
},
{
'catRefId' : "201425-1"
},
{
'catRefId' : "201423-1"
},
]
let arryTwo =
[
{
'removeId' : "200531-2"
},
{
'removeId' : "201425-1"
},
]
I tried below code but not working
let _finalArray = [];
for (let index = 0; index < arryOne.length; index++) {
_finalArray = arryOne.filter(obj => obj.catRefId === arryTwo[index].removeId)
}
Here's an approach using Set
let arryTwo = [{ removeId: '200531-2' }, { removeId: '201425-1'}];
let arryOne = [{ catRefId: '200531-2' }, { catRefId: '201425-1'},{ catRefId: '201423-1' }];
const idsToRemove = new Set(arryTwo.map(({ removeId }) => removeId));
const _finalArray = arryOne.filter((item) => !idsToRemove.has(item.catRefId));
console.log(_finalArray);
This is a possible solution:
First we extract the Ids from the second array.
Then we filter all matching references.
What's left is all references not present in arryTwo.
let _finalArray = [];
_finalArray = arryOne.filter(obj => !arryTwo.map(obj => obj.removeId).includes(obj.catRefId));

how to assign a key in object using javascript

I want to total all same ID and assign a specific key
var ingredArray= [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}]
let deduction={};
ingredArray.forEach(function (item) {
if (deduction.hasOwnProperty(item.inventory_id)) {
deduction[item.inventory_id] = deduction[item.inventory_id] + parseFloat(item.pergram);
} else {
deduction[item.inventory_id] = parseFloat(item.pergram);
}
});
console.log(deduction);
this is the result of my code
{1: 255, 2: 446, 3: 400, 5: 200}
I want to achieve
{"inventory_id":1,"pergram":255},{"inventory_id":2,"pergram":446},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}
Try this
var ingredArray = [{ "inventory_id": 1, "pergram": 222 }, { "inventory_id": 1, "pergram": 33 }, { "inventory_id": 2, "pergram": 424 }, { "inventory_id": 2, "pergram": 22 }, { "inventory_id": 3, "pergram": 400 }, { "inventory_id": 5, "pergram": 200 }]
var helper = {};
let deduction = ingredArray.reduce(function (r, o) {
var key = o.inventory_id;
if (!helper[key]) {
helper[key] = Object.assign({}, o); // create a copy of o
r.push(helper[key]);
} else {
helper[key].pergram += o.pergram;
}
return r;
}, []);
console.log(deduction);
reduce over the array of objects building a new object of summed values based on key, and then grab the Object.values.
const data = [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}];
const out = data.reduce((acc, c) => {
// Grab the id and pergram variables from the
// new object in the iteration
const { inventory_id: id, pergram } = c;
// If the the accumulator object doesn't have a key that
// matches the id create a new new object, and set the
// pergram variable to zero
acc[id] = acc[id] || { inventory_id: id, pergram: 0 };
// And then add the pergram value to the
// pergram object property
acc[id].pergram += pergram;
// Return the accumulator for the next iteration
return acc;
}, {});
console.log(Object.values(out));
Use Map to store the object reference based on unique inventory_id and call Array.reduce() method over list of objects.
var ingredArray= [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}];
const processArray = (list) => {
const map = new Map();
return list.reduce((accumulator, obj) => {
const inventory = map.get(obj.inventory_id);
if (inventory) {
inventory.pergram += obj.pergram;
} else {
accumulator.push(obj);
map.set(obj.inventory_id, obj);
}
return accumulator;
}, []);
}
console.log(processArray(ingredArray));

Reformatting array of arrays to nested json in Javascript [duplicate]

I have an array like
[
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1",
.
.
.
]
Wherein my first string before | is the parent and the second string before | is the child and the third string after the second | is the subchild
How can I convert this array into an object like
[
{
"id": "parent1",
"children":[
{
"id": "child1",
"children":[
{
"id": "subChild1"
}
]
}
]
}
]
Parent -> child -> subchild object
Based on Sebastian's answer I tried below using typescript
private genTree(row) {
let self = this;
if (!row) {
return;
}
const [parent, ...children] = row.split('|');
if (!children || children.length === 0) {
return [{
id: parent,
children: []
}];
}
return [{
id: parent,
children: self.genTree(children.join('|'))
}];
}
private mergeDeep(children) {
let self = this;
const res = children.reduce((result, curr) => {
const entry = curr;
const existing = result.find((e) => e.id === entry.id);
if (existing) {
existing.children = [].concat(existing.children, entry.children);
} else {
result.push(entry);
}
return result;
}, []);
for (let i = 0; i < res.length; i++) {
const entry = res[i];
if (entry.children && entry.children.length > 0) {
entry.children = self.mergeDeep(entry.children);
}
};
return res;
}
private constructTree(statKeyNames){
let self = this;
const res = this.mergeDeep(statKeyNames.map(self.genTree).map(([e]) => e));
console.log(res);
}
but this gives me:
Cannot read property 'genTree' of undefined" error
Update:
As per Sebastian's comment changed self.genTree to this.genTree.bind(this) and it worked without any issues
You could use a mapper object which maps each object to it's unique path (You could map the object with each id, but id is not unique here). Then reduce each partial item in the array. Set the root object as the initialValue. The accumulator will be the parent object for the current item. Return the current object in each iteration.
const input = [
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent1|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1"
],
mapper = {},
root = { children: [] }
for (const str of input) {
let splits = str.split('|'),
path = '';
splits.reduce((parent, id, i) => {
path += `${id}|`;
if (!mapper[path]) {
const o = { id };
mapper[path] = o; // set the new object with unique path
parent.children = parent.children || [];
parent.children.push(o)
}
return mapper[path];
}, root)
}
console.log(root.children)
You have to use recursion for that. Take a look here:
const arr = [
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1"
];
function genTree(row) {
const [parent, ...children] = row.split('|');
if (!children || children.length === 0) {
return [{
id: parent,
children: []
}];
}
return [{
id: parent,
children: genTree(children.join('|'))
}];
};
function mergeDeep(children) {
const res = children.reduce((result, curr) => {
const entry = curr;
const existing = result.find((e) => e.id === entry.id);
if (existing) {
existing.children = [].concat(existing.children, entry.children);
} else {
result.push(entry);
}
return result;
}, []);
for (let i = 0; i < res.length; i++) {
const entry = res[i];
if (entry.children && entry.children.length > 0) {
entry.children = mergeDeep(entry.children);
}
};
return res;
}
const res = mergeDeep(arr.map(genTree).map(([e]) => e));
console.log(JSON.stringify(res, false, 2));
I used two helpers here: genTree(row) which recursively generates a simple tree from each row, and mergeDeep(children) which reduces the first-level trees in the result of arr.map(genTree).map(([e]) => e), and then iterates over the array and recursively does the same thing to all children of each entry.

Javascript Array to Object

I have an array that looks like so:
files = [
'Dashboard/Logs/Errors',
'Dashboard/Logs/Other',
'Accounts/Main',
]
I want to make it look like this:
navigation = [
{
"title": "Dashboard",
"dropdown": [
{
"title": "Logs",
"dropdown": [
{
"title": "Errors",
},
{
"title": "Other",
}
]
}
]
},
{
"title": "Accounts",
"dropdown": [
{
"title": "Main",
}
]
}
]
I have the following so far:
var navigation = [];
for (var i = 0; i < files.length; i++) {
var parts = files[i].split('/');
navigation.push({title: parts[0]});
for (var j = 1; j < parts.length; j++) {
}
}
I am having difficulties figuring out a decent way to do this. What I have so far already doesn't work because it creates two objects under navigation each with title: "Dashboard". Any ideas for a clever approach? Thanks :)
This should produce the desired output:
var files = [
'Dashboard/Logs/Errors',
'Dashboard/Logs/Other',
'Accounts/Main',
];
var navigation = [];
// Iterates through a navigation array and returns the object with matching title, if one exists.
var getNavigationObject = function(nav, title) {
for (var i = 0; i < nav.length; i++) {
if (nav[i].title == title) {
return nav[i];
}
}
};
// Adds a file to the nav.
// The input is an array of file components (i.e. file.split('/'))
// This works by recursively adding each component of a file.
var addToNav = function (nav, components) {
var n = getNavigationObject(nav, components[0]);
if (!n) {
n = {
title: components[0]
};
nav.push(n);
}
if (components.length > 1) {
n.dropdown = n.dropdown || [];
addToNav(n.dropdown, components.slice(1));
}
};
// Actually call `addToNav` on each file.
files.forEach(function(e) {
addToNav(navigation, e.split('/'));
});
// Produces the result in string form.
JSON.stringify(navigation, null, 2)
This works by recursively checking if a given element already matches the component of the file. If it does, it recurs into that component's "dropdown". Otherwise, it creates it.
This is an approach with a temporary object and some array methods with no search overhead.
var files = ['Dashboard/Logs/Errors', 'Dashboard/Logs/Other', 'Accounts/Main'],
navigation = function (data) {
var r = [], o = {};
data.forEach(function (a) {
var s = r;
a.split('/').reduce(function (p, b) {
if (p.children) {
p.value.dropdown = p.value.dropdown || [];
s = p.value.dropdown;
p = p.children;
}
if (!(b in p)) {
p[b] = { value: { title: b }, children: {} };
s.push(p[b].value);
}
return p[b];
}, o);
});
return r;
}(files);
document.write('<pre>' + JSON.stringify(navigation, 0, 4) + '</pre>');

Categories

Resources