reducing an array of nested objects in javascript - nested

so i have this array and i want to return the sum of lci and the sum of mci with a reduce function
const data = [{ option:{ lci: 1, mci: 2 } }, { option:{ lci: 3, mci: 4 } }, { option:{ lci: 5, mci: 6 } }];
i have tried this but it's not working:
data.reduce( (previousValue, currentValue) => {
return {
totalLCI: previousValue.option.lci + currentValue.option.lci,
totalMCI: previousValue.option.lci + currentValue.option.lci,
};
})

reduce method takes 2 important args: your reducing function and the default value at step 0 of the iteration
Here the reducer has 2 args:
the so-called previousValue: here called reducerTarget as it receives what is returned in the body of the reducer
the currentValue where the reducer is: here called currentArrayItem
Here's what you might want
const {totalLCI, totalMCI} = data.reduce((reducerTarget, currentArrayItem) => {
return {
totalLCI: reducerTarget.totalLCI + currentArrayItem.option.lci,
totalMCI: reducerTarget.totalMCI + currentArrayItem.option.mci
}
}, {totalLCI: 0, totalMCI: 0})
From here, starting with the default value, what it does on the first iteration is
/* 1st step:
index = 0
targetReducer = defaultValue = {totalMCI: 0, totalLCI: 0}
currentArrayItem = {option: {lci: 1, mci: 2}}
*/
return {
totalLCI: 0 + 1,
totalMCI: 0 + 2
}
/* 2nd step:
index = 1
targetReducer = previousValue = {totalMCI: 1, totalLCI: 2}
currentArrayItem = {option: {lci: 3, mci: 4}}
*/
return {
totalLCI: 1 + 3,
totalMCI: 2 + 4
}

First you need know if you have stored value in the fields totalLCI and totalMCI, if they aren't set, add the previous value and the current value, after add, return the values, in the next iteration, you will check again if the data is stored in totalLCI and totalMCI, after that, you will get data stored in that field and sum it all the time till finish the iteration, here an example:
var data2 = [{ option:{ lci: 1, mci: 2 } }, { option:{ lci: 3, mci: 4 } }, { option:{ lci: 5, mci: 6 } }];
let test = data2.reduce( (previousValue, currentValue) => {
if(!previousValue.totalLCI && !previousValue.totalMCI){
return {
totalLCI: previousValue.option.lci + currentValue.option.lci,
totalMCI: previousValue.option.mci + currentValue.option.mci
};
}else{
return{
totalLCI: previousValue.totalLCI + currentValue.option.lci,
totalMCI: previousValue.totalMCI + currentValue.option.mci
}
}
});
console.log(test.totalLCI);
console.log(test.totalMCI);

Related

Inserting and deleting rows in excel with node.js cause problems

I have a node.js file whose function is to insert and delete columns in an excel file,Im working with the 'xslx' module.
Both insert and delete rows have their own function, so when I call one without calling the other, everything works fine, but if I call them at the same time they don't work as they should, the rows are repeated and not added as they should.
This is the data I want to put in the excel sheet :
let datos = [
{
"Dato 1" : 1,
"Dato 2" : 2,
"Dato 3" : 3
},
{
"Dato 1" : 4,
"Dato 2" : 5,
"Dato 3" : 6
},
{
"Dato 1" : 7,
"Dato 2" : 8,
"Dato 3" : 9
}
]
When I only call the function to put data, this happens Works fine it pull all the data in the correct way:
function caller() {
// console.log('Deleting previous data...')
// deleteAllExcelRows(woorksheet)
// console.log('Data eliminated\n')
console.log("Put new data...\n");
for (let i = 0; i < datos.length; i++) {
putDataExcel(woorksheet,datos,i);
}
}
caller()
When I only call the function to delete the data also works fine,deletes all the data except the headers:
function caller() {
console.log('Deleting previous data...')
deleteAllExcelRows(woorksheet)
console.log('Data eliminated\n')
// console.log("Put new data...\n");
// for (let i = 0; i < datos.length; i++) {
// putDataExcel(woorksheet,datos,i);
// }
}
caller()
Now if I called both something very weird happens, the last elements of the data is repeated:
function caller() {
console.log('Deleting previous data...')
deleteAllExcelRows(woorksheet)
console.log('Data eliminated\n')
console.log("Put new data...\n");
for (let i = 0; i < datos.length; i++) {
putDataExcel(woorksheet,datos,i);
}
}
caller()
INCORRECT RESULT :
Desire result :
Complete code :
const xlsx = require("xlsx");
const FILENAME = "C:\\Users\\harol\\Documents\\prueba-excel.xlsx"
let wb = xlsx.readFile(FILENAME);
let woorksheet = wb.Sheets[wb.SheetNames[0]];
let datos = [
{
"Dato 1" : 1,
"Dato 2" : 2,
"Dato 3" : 3
},
{
"Dato 1" : 4,
"Dato 2" : 5,
"Dato 3" : 6
},
{
"Dato 1" : 7,
"Dato 2" : 8,
"Dato 3" : 9
}
]
function putDataExcel(ws,data,i) {
let hoja = xlsx.utils.sheet_to_json(ws);
console.log(xlsx.utils.decode_range(ws["!ref"]))
hoja.push(data[i]);
xlsx.utils.sheet_add_json(ws, hoja);
xlsx.writeFile(wb, FILENAME);
}
function ec(row, column) {
return xlsx.utils.encode_cell({ r: row, c: column });
}
function deleteExcelRow(ws, row_index) {
let hoja = xlsx.utils.sheet_to_json(ws);
console.log(xlsx.utils.decode_range(ws["!ref"]))
let range = xlsx.utils.decode_range(ws["!ref"]);
for (var R = row_index; R < range.e.r; ++R) {
for (var C = range.s.c; C <= range.e.c; ++C) {
ws[ec(R, C)] = ws[ec(R + 1, C)];
}
}
range.e.r--;
ws["!ref"] = xlsx.utils.encode_range(range.s, range.e);
xlsx.writeFile(wb, FILENAME);
console.log(xlsx.utils.decode_range(ws["!ref"]))
console.log("\n")
}
function deleteAllExcelRows(ws)
{
let numeroFilas = parseInt(xlsx.utils.decode_range(ws['!ref']).e.r)
for(let i = 1;i<=numeroFilas;i++)
{
deleteExcelRow(ws,i);
console.log("Dato " + i + "Eliminado ")
}
}
function caller() {
console.log('Deleting previous data...')
deleteAllExcelRows(woorksheet)
console.log('Data eliminated\n')
console.log("Put new data...\n");
for (let i = 0; i < datos.length; i++) {
putDataExcel(woorksheet,datos,i);
}
}
caller()
Why this happens and how I can solve it ?

Compare two object key values, return minimum denominator

I am new to NodeJS coming from Python and having trouble with this practice problem.
Given two objects (key, value pairs) return the minimum denominator between them.
ObjectA = {"A": 10, "B": 20, "C": 30} "Stock"
ObjectB = {"A": 5, "B": 10, "C": 1} "Per Item"
function(objectA, objectB) would return 2 since ObjectB can go into ObjectA "fully" 2 times.
In my mind or Python, I would for loop each key/value compare the two objects. Not sure how to do this in a performance way/deal with it if key-values are out of order.
Unsure how to iterate through these objects in JavaScript, would I have to call each child independently? How do I index them?
main.js:
function howMany(params) {
const {objectA, objectB} = params
Object.keys(objectA).reduce((denominator, key) => {
if(objectB[key] === undefined) {
return 0;
}
const d = Math.floor(objectA[key] / objectB[key]);
return d < denominator ? d : denominator;
}, Infinity);
}
module.exports = {howMany}
var params = require('./inputs.js');
howMany(params.returnsOne);
console.log(howMany(params.returnsOne));
inputs.js:
module.exports = {
returnsOne: {
objectA: {
a: 10,
b: 5,
c: 5
},
objectB: {
a: 20,
b: 10,
c: 5
}
}
}
You could use the spread syntax to pass all the ratios to Math.min and then truncate the result:
var objectA = { A: 10, B: 20, C: 30 },
objectB = { A: 5, B: 10, C: 1 },
denominator = Math.floor(Math.min(...Object.keys(objectA).map(
key => objectA[key] / objectB[key] ))) || 0;
console.log(denominator);
More integrated with what you have as context:
function howMany({objectA, objectB}) {
return Math.floor(Math.min(...Object.keys(objectA).map(
key => objectA[key] / objectB[key] ))) || 0;
}
const params = {
returnsOne: {
objectA: { A: 10, B: 20, C: 30 },
objectB: { A: 5, B: 10, C: 1 }
}
};
console.log(howMany(params.returnsOne));
You could reduce the keys and take the one with the smalles value of the division.
var objectA = { A: 10, B: 20, C: 30 },
objectB = { A: 5, B: 10, C: 1 },
keys = Object.keys(objectA),
key = keys.reduce(function (a, b) {
var aa = Math.floor(objectA[a] / objectB[a]),
bb = Math.floor(objectA[b] / objectB[b]);
return aa < bb ? a : b;
}),
denominator = Math.floor(objectA[key] / objectB[key]);
console.log(denominator);
Iterate the Object#keys of ObjectA with Array#reduce, and on each iteration assign the denominator to the current lowest, or to 0 if key is missing in ObjectB:
const ObjectA = {"A": 10, "B": 20, "C": 30};
const ObjectB = {"A": 5, "B": 10, "C": 1};
const findBinA = (objectA, objectB) => Object.keys(objectA)
.reduce((denominator, key) => {
const d = Math.floor(objectA[key] / objectB[key]) || 0;
return d < denominator ? d : denominator;
}, Infinity);
console.log(findBinA(ObjectA, ObjectB));

Fill an array asynchronously

I have two arrays here, filling one of them with random numbers:
var arr1 = [1];
var arr2 = [];
function arr1Filler() {
arr2 = arr1;
setTimeout(function() {
for (var i = 0; i < 3; i++) {
arr1[i] = Math.floor(Math.random()*5);
}
console.log('array 1:');
console.log(arr1);
console.log('array 2:');
console.log(arr2);
}, 500);
}
setInterval(arr1Filler, 1000);
in this example, why these two arrays are always equal. In other words, why I am getting something like this:
array 1:
[ 3, 0, 1 ]
array 2:
[ 3, 0, 1 ]
array 1:
[ 0, 2, 3 ]
array 2:
[ 0, 2, 3 ]
array 1:
[ 1, 2, 4 ]
array 2:
[ 1, 2, 4 ]
instead of a result like this (the last value of array 2 is the new value of array 1):
array 1:
[ 1 ]
array 2:
[ 3, 0, 1 ]
array 1:
[ 3, 0, 1 ]
array 2:
[ 0, 2, 3 ]
array 1:
[ 0, 2, 3 ]
array 2:
[ 1, 2, 4 ]
What should I do to get the second result which I expect?
They are the same because you set the arrays to the same object here:
arr2 = arr1;
So when you add to arr1, arr2 is always identical. What you want is a copy of arr1. You can use slice to effectively make a copy of an array.
arr2 = arr1.slice();
var arr1 = [1];
var arr2 = [];
function arr1Filler() {
arr2 = arr1.slice();
setTimeout(function() {
for (var i = 0; i < 3; i++) {
arr1[i] = Math.floor(Math.random()*5);
}
console.log('array 1:');
console.log(arr1);
console.log('array 2:');
console.log(arr2);
}, 500);
}
setInterval(arr1Filler, 1000);

Couchbase/CouchDB grouping by part of key and ordering by another part

I've got a database of messages between users. Document looks like:
"_id": "msg_0bec56c1-cbfb-47a5-8882-4a61fec332cd",
"_rev": "1-00000eda4d07c93d0000009100000112",
"$flags": 0,
"$expiration": 0,
"Date": 1340280439303,
"OwnerId": 35,
"RcptId": 37,
"SndrId": 35,
"Text": "msg5",
"Unread": false,
"id": "0bec56c1-cbfb-47a5-8882-4a61fec332cd",
"type": "msg"
For each message it stores 2 documents with different OwnerId. I need to get the lastest message between one specified person and 10 uniqe "pen friends" ordered by last message date.
My query params:
descending=true&endkey=[35,35]&startkey=[35,35,"{}"]&limit=10&skip=0
My map function:
function (doc) {
if (doc.type == "msg"){
emit([doc.OwnerId, doc.SndrId, doc.Date, doc.RcptId], doc);
emit([doc.OwnerId, doc.RcptId, doc.Date, doc.SndrId], doc);
}
}
As a result I get list of messages for desired account. Last value (4th) in key is that value on which we should group values.
Groupping with group level 4 not working because dates are different.
Here is my new map function that does exactly what i've wanted BUT only if I have one server.
var uniqPairs = [];
function(doc){
if (doc.type == "msg"){
if (doc.OwnerId == doc.SndrId){
if (uniqPairs.indexOf(doc.OwnerId + "_" + doc.RcptId) == -1){
uniqPairs.push(doc.SndrId + "_" + doc.RcptId);
uniqPairs.push(doc.RcptId + "_" + doc.SndrId);
emit([doc.OwnerId, doc.Date], {"owner": doc.OwnerId, "from":doc.SndrId, "to":doc.RcptId});
}
}
if (doc.OwnerId == doc.RcptId){
if (uniqPairs.indexOf(doc.OwnerId + "_" + doc.SndrId) == -1){
//uniqPairs.push(doc.SndrId + "_" + doc.RcptId);
uniqPairs.push(doc.RcptId + "_" + doc.SndrId);
emit([doc.OwnerId, doc.Date], {"owner": doc.OwnerId, "from":doc.SndrId, "to":doc.RcptId});
}
}
}
}
So for cluster I sacrificed "ordering by date" and got such functions for map/reduce:
Map:
function (doc) {
if (doc.type == "msg"){
if (doc.OwnerId == doc.SndrId){
emit([doc.OwnerId, doc.RcptId, doc.Date], {"Date":doc.Date, "OwnerId":doc.OwnerId, "RcptId":doc.RcptId, "SndrId":doc.SndrId, "Text":doc.Text, "Unread":doc.Unread, "id": doc.id, "type":doc.type});
} else {
emit([doc.OwnerId, doc.SndrId, doc.Date], {"Date":doc.Date, "OwnerId":doc.OwnerId, "RcptId":doc.RcptId, "SndrId":doc.SndrId, "Text":doc.Text, "Unread":doc.Unread, "id": doc.id, "type":doc.type});
}
}
}
Reduce:
var tmp = [];
var t = [];
function findRecent(arr){
var max = 0;
var maxId = 0;
for (var i in arr){
if (arr[i].Date > max){
max = arr[i].Date;
maxId = i;
}
}
return arr[maxId];
}
function(k,v,r){
if (!r){
tmp.push(v);
}
else{
tmp.push(v);
}
if (r){
for (var i1 in tmp[0])
{
t.push(tmp[0][i1]);
}
} else {
return findRecent(v);
}
return findRecent(t);
}
If someone knows better solution (i.e. how to oreder results by date) - you're welcome to answer.

Couchdb rereduce understanding and sum

I don't know if this is really rereduce but that's why I'm here.
I have this:
"afrikan" ID: f6733302df85ac109397f4b6030005bf [1, 2]
"afrikan" ID: f6733302df85ac109397f4b6030006d1 [1, 3]
"afrikan" ID: f6733302df85ac109397f4b6030012b6 [2, 4]
"chinese" ID: f6733302df85ac109397f4b6030012eb 1
"chinese" ID: f6733302df85ac109397f4b603001d87 1
I know how to calculate the sum if the value wasn't an array (like in
chinese). But I can't manage to sum the values in the array and it
results in this: "afrikan" "02,41,31,2"
And one more question: is there a way to sum all the values regardless
of keys?
View code:
function(doc) {
if(doc.food) {
emit(doc.food, doc.type);
}
}
Reduce code:
function(keys,values,rereduce)
{
return sum(values);
}
Thank you a lot :)
UPDATE:
I found an answer for Couchdb.
Here is the code:
function(doc) {
if(doc.food) {
if(doc.type.length>1) {
doc.type.forEach(function(tag) {
emit(doc.food,tag);
});
}else {
emit(doc.food,doc.type);
}
}
}
Reduce:
function(keys,values) {
return sum(values);
}
The reason you get "02,41,31,2" is because Javascript will convert a number + an array into a string. It is very strange, but true.
> 0 + [2, 4] + [1, 3] + [1, 2]
'02,41,31,2'
> sum([0, [2, 4], [1, 3], [1, 2]]) // Same result
'02,41,31,2'
I think you do not need to worry about rereduce. Just add every value together. If it is an array, add all the values in the array.
function(keys, values, rereduce)
{
var total = 0;
var i, j;
for(i = 0; i < values.length; i++) {
if(typeof values[i] == "number") {
total = total + values[i];
}
else {
for(j = 0; j < values[i].length; j++) {
total = total + values[i][j];
}
}
}
}

Resources