How to sum all values in column? - javascript

I'm using exceljs and i must sum all values from my column, how can i do this?
at issue on github, i found one solution, but not work for me:
workSheet.getCell(`B${endRow}`).value = { formula: `SUM(B4:B${endRow-1})` };
because vscode throw me: Type '{ formula: string; }' is not assignable to type 'CellValue'. Type '{ formula: string; }' is missing the following properties from type 'CellSharedFormulaValue': sharedFormula, date1904
can somebody tell me how to sum each values from column?

Ciao, try to modify your code like this:
workSheet.getCell(`B${endRow}`).value = { formula: `SUM(B4:B${endRow-1})`, date1904: false };

Im a bit late im adding this for anyone who came across the same issue.
point number one your array object values must be of type number not string.
I created a method to do that for me which is convertStringToNumber(data);
Example data
[{ItemPrice: 69.99, name: "Kellogs Cornflakes", brand: "Kellogs", Quantity_Purchased: 2, QaunititySaleValue: 139.98}, {ItemPrice: 19.99, name: "Castle Lite", brand: "Castle", Quantity_Purchased: 2, QaunititySaleValue: 39.98}]
Code
async createExcel(data, fileName) {
let xlsData = this.convertStringToNumber(data);
const fs = require('fs')
const workbook = new Excel.Workbook();
const worksheet = workbook.addWorksheet(fileName);
worksheet.columns = [
{ header: 'name', key: 'name', width: 10 },
{ header: 'brand', key: 'brand', width: 32 },
{ header: 'Quantity_Purchased', key: 'Quantity_Purchased', width: 15, },
{ header: 'ItemPrice', key: 'ItemPrice', width: 15, },
{ header: 'QaunititySaleValue', key: 'QaunititySaleValue', width: 15, }
];
worksheet.addRows(xlsData);
const endRow = worksheet.lastRow._number + 1;
worksheet.getCell(`C${endRow}`).value = { formula: `SUM(C2:C${endRow - 1})` };
worksheet.getCell(`D${endRow}`).value = { formula: `SUM(D2:D${endRow - 1})` };
worksheet.getCell(`E${endRow}`).value = { formula: `SUM(E2:E${endRow - 1})` };
// save under export.xlsx
let buffResult = await workbook.xlsx.writeBuffer();
fs.writeFileSync(fileName + ".xlsx", buffResult); }
convertStringToNumber(objects) {
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && obj[prop] !== null && !isNaN(obj[prop])) {
obj[prop] = +obj[prop];
}
}
}
return objects; }
Output

Related

How to invert the structure of nested array of objects in Javascript?

I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key

Finding Maximum value in an Array of Objects where numer is a part of string, most optimal solution and write tests

I have this code which finding max value from object array where number is part of string:
var stringArray = [
{ name: 'string 1' },
{ name: 'string 2' },
{ name: 'string 11' },
{ name: 'string 3' },
{ name: 'string 10' }
];
var customModuleRe = new RegExp('\\d+');
var getMax = function () {
let max = 0, current;
for (let i = 0, count = stringArray.length; i < count; i++) {
current = customModuleRe.exec(stringArray[i].name);
if (current) {
let num = parseInt(current[0]);
if (!isNaN(num) && num > max) {
max = num;
}
}
}
return max;
};
I must change this code to be clean, the most optimal and readable. In my opinion i need to use map function but i don't know how i can find max value. I've made this:
let max, current;
getMax.map(function(a) {
current = customModuleRe.exec(a.name);
if (current) {
let num = parseInt(current[0]);
//max
}
});
Can someone help me to find the fatest solution? Maybe i need to use reduce()?
You can use Math.max with spread syntax and map() method.
var stringArray = [
{ name: 'string 1' },
{ name: 'string 2' },
{ name: 'string 11' },
{ name: 'string 3' },
{ name: 'string 10' }
];
var max = Math.max(...stringArray.map(e => e.name.match(/\d+$/)))
console.log(max)

Better way to map an array of objects to two arrays, one of unique keys and another of number of occurences

I have an array of objects, and I'm struggling to come up with a better way to map it to two new arrays: one containing a list of unique code values, and the other containing a count of occurrences of each code.
Note: The order of the resulting arrays are important. For instance, data[1] should be the number of occurrences of labels[1].
This is what I have so far, but I can't seem to find a way to do it without multiple loops...
var objs = [{
code: 200,
message: 'a'
},
{
code: 300,
message: 'b'
},
{
code: 200,
message: 'c'
},
{
code: 400,
message: 'd'
},
{
code: 200,
message: 'e'
},
];
var data = [];
var labels = [];
var bins = objs
.reduce((codes, obj) => {
let key = obj.code;
codes[key] ? codes[key]++ : codes[key] = 1;
return codes;
}, {});
for (var prop in bins) {
if (!bins.hasOwnProperty(prop))
continue;
labels.push(prop);
data.push(bins[prop]);
}
console.log('data', data); // [3, 1, 1]
console.log('labels', labels); // ['200', '300', '400']
You can use reduce the array of objects into a Map, and then extract the values and the keys into arrays:
const values = [{ code: 200, message: 'a' }, { code: 300, message: 'b' }, { code: 200, message: 'c' }, { code: 400, message: 'd' }, { code: 200, message: 'e' }];
const counts = values.reduce((m, { code }) => m.set(code, (m.get(code) || 0) + 1), new Map());
const data = [...counts.values()];
const label = [...counts.keys()];
console.log(data);
console.log(label);
A little more clean using a newer version of Javascript, should work the same though. Check it out.
const objs = [{
code: 200,
message: 'a'
},
{
code: 300,
message: 'b'
},
{
code: 200,
message: 'c'
},
{
code: 400,
message: 'd'
},
{
code: 200,
message: 'e'
},
];
const data = [];
const labels = [];
objs.forEach(obj => {
if (labels.indexOf(obj.code) < 0) {
labels.push(obj.code);
data.push(1);
} else {
const index = labels.indexOf(obj.code);
data[index] ++;
}
});
console.log('data', data);
console.log('labels', labels);
This look like you want some object which is like an array and can have key-value.
I recommend using Map
have a look here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
with this you could make this with one loop and receive the correct result you needed
You could take a hash table for the indices for updating the arrays.
var values = [{ code: 200, message: 'a' }, { code: 300, message: 'b' }, { code: 200, message: 'c' }, { code: 400, message: 'd' }, { code: 200, message: 'e' }],
hash = Object.create(null),
data = [],
label = [];
values.forEach(function (o) {
if (o.code in hash) {
data[hash[o.code]] += 1;
return;
}
hash[o.code] = data.push(1) - 1;
label.push(o.code);
});
console.log(data);
console.log(label);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Flattening object to base level with ES6

How would you get this object:
const data = {
importantData: {
index: 0,
about: 'test',
':sub': {
index: 1
}
},
telephone: {
index: 2,
nr: 1234567
}
}
to this:
{
importantData: {
index: 0,
about: 'test'
},
':sub': {
index: 1
}
telephone: {
index: 2,
nr: 1234567
}
}
wtih ES6.
I had success with finding the '.sub' object and getting its properties. However, I could not find a way to delete it from 'importantData' object for what I created new object with assign and saved non-':sub' values.
Here is the unoptimized solution.
function flattenToOneLevel (data) {
for (let key in data) {
const selectorName = key;
const styles = data[key];
for (let _key in styles) {
if (Object.prototype.toString.call(styles[_key]) === '[object Object]') {
data[_key] = styles[_key];
delete data[key][_key];
}
}
}
return data;
}

Get string representation of JavaScript object

I have a object like so:
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95,
...
},
table: {
productType: 'GM',
categoryId: 1,
...
}
}
};
How do I get a string that represents the whole object in dot notation? e.g.
query.filter.table.productType
To clarify, I am using this string value as a key to store a key/value pair in localStorage.
I am using angular to $wacth each property on the object for a change. Since you can't watch an object and know which property changed with watching all, I need to get creative and store each property in a key/value pair.
You can do it recursively, and produces "key" in an array.
var obj = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var stringify = function (e) {
var rs = [];
for (var k in e) {
if (e.hasOwnProperty(k)) {
if (typeof e[k] == 'object') {
var l = stringify(e[k]);
for (var i = 0; i < l.length; i++) {
rs.push(k + '.' + l[i]);
}
} else {
rs.push(k);
}
}
}
return rs;
}
console.log(stringify(obj));
outputs:
["query.filter.table.productType"]
fiddle
Demo
Before Ques Edit
var $scope = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var k = JSON.stringify($scope)
//output "{"query":{"filter":{"table":{"productType":"GM"}}}}"
k.match(/\w+(?=\"\:)/g).join('.')
//output"query.filter.table.productType"
Edit
Updated Demo
If OP has no issue with the position of child elements
var $scope = {}
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1,
}
}
};
k=JSON.stringify($scope)
{"query":{"filter":{"column":{"productName":"Some Product","price":29.95},"table":{"productType":"GM","categoryId":1}}}}
k.match(/\w+(?=\"\:)/g).join('.')
"query.filter.column.productName.price.table.productType.categoryId"
By iterating the properties into an array recursively you could create a hierarchical structure that represents the data in the object. From here you could parse the results out as you wish.
var scope = {
query: {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1
}
}
}
};
function buildProps(subject) {
var result = [];
for (var key in subject) {
if (subject.hasOwnProperty(key)) {
if (typeof subject[key] == "object") {
result.push(key, buildProps(subject[key]));
} else {
result.push(key);
}
}
}
return result;
}
function stringify(input) {
var result = [];
for (var i = 0; i < input.length; i++) {
if (typeof input[i] == "string") {
result.push(input[i]);
} else {
result = result.concat(stringify(input[i]));
}
}
return result.join('.');
}
console.log(buildProps(scope));
console.log(stringify(buildProps(scope)));
Parse out the strings in the resulting array/sub-arrays, format it any way you like.
In my simple example I just list them in order:
query.filter.column.productName.price.table.productType.categoryId

Categories