calculate average result from multi-dimensionally sorted array (JavaScript) - javascript

Below is the layout of my JSON File.
{
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
I'm trying to retrieve a single array of values by looping through each question, indexed by an "orgs" value and then adding each value retrieved and dividing it by data.dates.length.
Here is my code;
d3.json("data.json", function(error, data) {
var array = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
for (var question = 0; question < data.questions.length; question++) {
array.push(
data.values[question][org]
)
console.log(array)
}
// add array together
array.reduce(function(a, b) {
return a + b;
})
// calculate average
var avg = array / data.dates.length;
})
Here is a plnk;
http://plnkr.co/edit/wMv8GmkD1ynjo9WZVlMb?p=preview
I think the issue here is how I'm retrieving the values in the first place? as at the moment, although I am retrieving the correct values in the console log, I'm getting the array twice, and both times inside nested arrays. I'm not so sure how to remedy the problem?
For reference;
[question1][org1] corresponds to the values [5, 88, 18].
Hope someone can offer some advice here?
Thanks!

Since you clarified your question to indicate you want to calculate separate averages for each question, I've rewritten my answer. You should do all the calculations in the for loop, since the loop is looping through the questions. Then store your averages in an array.
d3.json("data.json", function(error, data) {
var averages = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
var values, sum;
for (var question = 0; question < data.questions.length; question++) {
// get the values for the question/org
values = data.values[question][org];
// calculate the sum
sum = values.reduce(function(a, b) {
return a + b;
});
// calculate the average
averages.push(sum / values.length);
}
console.log(averages);
});

Perform the .reduce() in the for loop and push that result into array. That will give you the an array of the results you expected.
array.push(data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length)
[
47.666666666666664,
43.666666666666664
]
Currently, you're attempting to perform addition on the arrays themselves in the .reduce() callback instead of reducing the members of each individual array to their sum, and then average.
Demo: (Click the text below to show the whole function)
var data = {
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
x(data)
// Your callback function.
function x(data) {
var array = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
for (var question = 0; question < data.questions.length; question++) {
array.push(data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length)
}
console.log(array)
}
Instead of a for loop, you could also use .map().
var array = data.questions.map(function(_, question) {
return data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length
})
Demo: (Click the text below to show the whole function)
var data = {
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
x(data)
// Your callback function.
function x(data) {
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
var array = data.questions.map(function(_, question) {
return data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length
})
console.log(array)
}

You need to store the sum, the result of reduce.
// add array together
// store in sum
var sum = array.reduce(function(a, b) {
return a + b;
}, 0); // use 0 as start value
For the average, you do not need the length of data.dates but from array, because you collecting the values and this length is important.
// calculate average
var avg = sum / array.length;
Together for all values, you might get this
var data = { "questions": ["Question1", "Question2"], "orgs": ["Org1", "Org2", "Org3"], "dates": ["Q1", "Q2", "Q3"], "values": [[[5, 88, 18], [50, 83, 10], [29, 78, 80]], [[46, 51, 61], [95, 21, 15], [49, 86, 43]]] },
sum = [];
data.values.forEach(function (a, i) {
sum[i] = sum[i] || [];
a.forEach(function (b) {
b.forEach(function (c, j) {
sum[i][j] = sum[i][j] || 0;
sum[i][j] += c;
});
});
});
data.avg = sum.map(function (a, i) {
return a.map(function (b) {
return b / data.values[i].length;
});
});
console.log(sum);
console.log(data);

Related

best way to search and delete with given values, for delete from array inside the object and return new array of object javascript?

I have tried a couple of methods using findIndex, map, Object.entires
someone help me find the best solution?
**
remove 2 from customerNumber array [1,2,3,4,5]
set to customerNumber value with array[1,3,4,5]
and spread customerNumber to filterState array
**
let filterState = [
{'customerNumber': [1,2,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
search and delete 2 from customerNumber//customerNumber : 2
function removedChip(type='', value=0, filterState=[]) {
for(let i=0; i<filterState.length; i++) {
let entries = Object.keys(filterState)
.forEach(function eachKey(key) {
console.log(key); // alerts key
console.log(filterState[key]); // alerts value
});
console.log(entries)
let searchIndex = entries.findIndex(type);
console.log('searchIndex', searchIndex)
console.log('type of ', filterState[searchIndex])
for(let i=0; i<filterState[searchIndex]; i++) {
//remove 2 from customerNumber array [1,2,3,4,5]
// set to customerNumber value with array[1,3,4,5]
// and spread customerNumber to filterState array
}
}
}
function invoking with values
removedChip('customerNumber', 10, filterState)
the expected output is
let filterState = [
{'customerNumber': [1,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
This might help :
function removedChip(type='', value=0, filterState=[]) {
const filterStateTypeArray = filterState.filter(fs =>
Object.keys(fs)[0] === type);
const filterStateTypeItem = filterStateTypeArray ?
filterStateTypeArray[0] : null;
if(!filterStateTypeItem){return;}
let valueArray = filterStateTypeItem[type];
valueArray = valueArray.filter(vA => vA !== value);
filterStateTypeItem[type] = valueArray;
console.log(filterState);
}
let filterState = [
{'customerNumber': [1,2,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
removedChip('customerNumber', 2, filterState);
Not much of a change from other answers which are all feasible - I'd just split out the functions in 2 to have the filtering handled for an array which can then be tested independently of the parent function or independently from whatever list of objects is inputted
I.e you can have a generic filtering method that can be tested in isolation from the input list of objects.
let filterState = [
{ customerNumber: [1, 2, 3, 4, 5] },
{ ward: [10, 20, 30, 40, 50] },
{ enrolledDate: [111, 222, 333, 444, 555] },
{ district: ['AAA', 'BBB', 'CCCC', 'DDD'] },
{ onBoardingSmsStatus: false },
];
// Independent testable filtering
const removeChipFromArray = (array, removeValue = 0) => {
return array.filter(e => e !== removeValue);
};
// Function to handle the removal of any value from any list of objects
const removeChips = (listOfObjects, value) => {
listOfObjects.forEach((element, index) => {
const key = Object.keys(element);
// General type checker (which would be easier in TS than JS but here is a half-safe way of ensuring you're operating against a list
// You could also convert it to an Array if you think that's better
if (typeof(element[key]) === 'object') {
element[key] = removeChipFromArray(element[key], value);
}
});
};
removeChips(filterState, 2);
console.log(filterState);
In your removedChip You can filter it like..
function removedChip(type = "", value = 0, filterState = []) {
const result = filterState.map((data) => {
if (data[type]) {
// Modify only the given field in the type params
return { [type]: data[type].filter((du) => du !== value) };
}
return data;
});
return result;
}
let filterState = [
{ customerNumber: [1, 2, 3, 4, 5] },
{ ward: [10, 20, 30, 40, 50] },
{ enrolledDate: [111, 222, 333, 444, 555] },
{ district: ["AAA", "BBB", "CCCC", "DDD"] },
{ onBoardingSmsStatus: false }
];
console.log(removedChip("customerNumber", 2, filterState));

max with condition in Javascript

I have two arrays:
search array: [['#S!', 1, 1], ['#$#', 2, 5], ['#S!', 10, 12], ['#$#', 21, 5]]
and key array: ['#S!','#$#']
I want to look up into search array based on the key array element and create a resultant array which looks like this:
[[key array element,max while lookup for value at index 1 in search array, max while lookup for value at index 2 in search array], [...]]
Here is my code for the same:
let resultant = [];
keys.forEach((ele, ind) => {
resultant[ind] = [
ele,
Math.max(searchArray.filter(element => element[0] === ele)),
Math.max(searchArray.filter(element => element[0] === ele))
];
});
Now I am confused in these statements:
Math.max(newSet.filter(element => element[0] === ele)),
Math.max(newSet.filter(element => element[0] === ele))
Because filter will return the entire array but I want to find max of element with index 1 and in second statement I want to return max of element with index 2 which have the element with index 0 same as the key which I have provided.
Here is one simple test case:
search Array: [["A",1,2],["A",12,23],["A",11,23],["A",14,42],["A",71,32],["B",113,42],["B",145,62],["C",91,32],["C",14,222],["C",111,2]]
keys Array: ["A","B","C"]
Output: [["A",71,42],["B",145,62],["C",111,222]]
As you can see max corresponding to the elements are mapped to the same elements. Can someone help me with this? Is there a better or more optimized algorithm for the same than what I am using?
You could take a dynamic approach with an object for the wanted keys.
function max(search, keys) {
const temp = search.reduce((r, [key, ...data]) => {
if (!r[key]) r[key] = [key, ...data];
else data.forEach((v, i) => { if (r[key][i + 1] < v) r[key][i + 1] = v; });
return r;
}, {});
return keys.map(key => temp[key]);
}
console.log(max([['#S!', 1, 1], ['#$#', 2, 5], ['#S!', 10, 12], ['#$#', 21, 5]], ['#S!','#$#']));
console.log(max([["A", 1, 2],["A", 12, 23],["A", 11, 23], ["A", 14, 42], ["A", 71, 32], ["B", 113, 42], ["B", 145, 62], ["C", 91, 32], ["C", 14, 222], ["C", 111, 2]], ["A", "B", "C"]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try use js array flatten method to do this,
let searchArray = [["A",1,2],["A",12,23],["A",11,23],["A",14,42],["A",71,32],["B",113,42],["B",145,62],["C",91,32],["C",14,222],["C",111,2]];
let keysArray = ["A","B","C"];
console.clear();
let output = [];
keysArray.forEach(item => {
let groupSearchArray = searchArray.filter(f => f[0] === item);
let sortedArray = groupSearchArray.flat(1).filter(f => f !== item).sort().reverse();
output.push([item, sortedArray[0], sortedArray[1]]);
});
console.log(output);

How does one process and combine nested array items into a new structure based on requirements?

I'm trying to sum the indices of an array and save them into a new one.
I have included an example array below, as well as my first try to solve this:
var data = [
["29.01.2020", 40, 10],
["30.01.2020", 80, 100],
["31.01.2020", 90, 500],
["01.02.2020", 30, 205],
["02.02.2020", 10, 74],
["03.02.2020", 30, 120]
];
The results I'm trying to create are something like these (either an array or an object):
[['0120', 210, 610], ['0220', 70, 399]]
{
"0120": {
"abc": 210,
"def": 610
},
"0220": {
"abc": 70,
"def": 399
}
}
This is something I found and was playing around with, perhaps something that might work with a little editing:
var res = {};
data.forEach((item)=>{
if(Object.keys(res).includes(item[0])){
res[item[1]] += item[1];
} else {
res[item[0]] = item[1];
}
});
Shortening the first index of my data array should be possible by using substrings I guess? What I'm mainly struggling with is getting it to summarize the index.
Any ideas how I can get there?
Thanks in advance
First, you can get the key from the first element of each subarray using String.split function. Using split function, you can extract the month and year string and combine it to generate the key of each array.
And based on that key, using Array.prototype.reduce, you can generate the content you want.
const data = [
["29.01.2020", 40, 10],
["30.01.2020", 80, 100],
["31.01.2020", 90, 500],
["01.02.2020", 30, 205],
["02.02.2020", 10, 74],
["03.02.2020", 30, 120]
];
const result = data.reduce((acc, cur) => {
const dateArr = cur[0].split('.');
const key = dateArr[1] + dateArr[2].slice(-2);
if (acc[key]) {
acc[key].abc += cur[1];
acc[key].def += cur[2];
} else {
acc[key] = {
abc: cur[1],
def: cur[2]
};
}
return acc;
}, {});
console.log(result);
You can use array reduce method. Split the date string by dot. Then make it a key(according to your requirement two digit from month and last two digit from year) and based on that key just count it. At last using Object.values output the array.
const data = [
['29.01.2020', 40, 10],
['30.01.2020', 80, 100],
['31.01.2020', 90, 500],
['01.02.2020', 30, 205],
['02.02.2020', 10, 74],
['03.02.2020', 30, 120],
];
const ret = data.reduce((prev, c) => {
const p = prev;
const date = c[0].split('.');
const key = `${date[1]}${date[2].slice(-2)}`;
if (!p[key]) p[key] = [key, c[1], c[2]];
else p[key] = [key, p[key][1] + c[1], p[key][2] + c[2]];
return p;
}, {});
console.log(Object.values(ret));

loop through 2 arrays and array of objects and inserting the elements of array 1 and array 2 into the array of object's certain keys

I have an array of objects and an array. My intention is to loop through both and inserting each element of the array into a certain key into the array of objects.
My intention to dynamically insert the values of each array
Notes for Clarification:
arr1 length = 44
arr2 length = 44
arrOfObj length = 44
my thought process of the operation:
loop through arr1 either by forEach or for
loop through arr2
loop through arrOfObject
insert into arrOfObject[i].labels arr1[i]
insert into arrOfObject[i].values arr2[i]
an example:
Array 1
const arr = [ 76, 72, 69, 66, 66, 66, 65, 65, 64, 64, 64, 63, 61, 61, 61, 61, 61, 61, 60, 59, 59, 59, 58, 58, 57, 57, 56, 56, 56, 55, 54, 54, 53, 52, 52, 51, 51, 50, 50, 49, 49, 49, 47, 47]
Array 2
const arr2 = [ "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9", "Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17","Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24", "Item25","Item26", "Item27", "Item28", "Item29", "Item30", "Item31", "Item32", "Item33", "Item34", "Item35", "Item36", "Item37", "Item38", "Item39", "Item40", "Item41", "Item42", "Item43", "Item44]
Array of object
const arrOfObj = [
{
labels:[],
values: [],
},
{
labels:[],
values: [],
},
{
labels:[],
values: [],
},
]
The desired output is
const arrOfObj = [
{
labels:[arr1[0]],
values: [arr2[0]],
},
{
labels:[arr1[1]],
values: [arr2[1]],
},
]
-outcome
const arrOfObj = [
{
labels:['item1'],
values: [76],
},
{
labels:['item2'],
values: [72],
},
]
If there is a way to do it without nested loops (maybe one loop for values insertion and a separate one for labels), as nested loops decrease the execution performance, then it is preferable unless it is a necessity then it is okay.
As well if you a good source for objects and arrays manipulation above the basics level it will great to share
so far my work can be viewed on
https://codesandbox.io/s/epic-archimedes-8kg6h?eslint=1&expanddevtools=1&fontsize=14&hidenavigation=1&theme=dark
thanks a lot in advance
Reworked the original answer to this function.
It will dynamically do what you intended for x array with x length
function combineArr(target, ...arrays){
// looping through the target array
target.map((ele, superIndex)=>{
// looping through the attributes of the target
Object.keys(ele).map((key, index)=>{
// writing the results
target[superIndex][key] = arrays[index][superIndex]
})
})
return target;
}
console.log(combineArr(arrayOfObj,arr1,arr2))
target is your arrOfObj, and arrays will be arr1, arr2
So what it boils down to is that your arrOfObj shares an index with arr1 and arr2. arrOfObj[i] arr1[i] arr2[i] but since arrOfObj[i] is an object with keys we make it iterable with Object.keys and make the arrays iterable aswell with ...arrays so that keys[i] shares index with arrays[i]
Edit: I was able to reproduce the error that was present in your link. The generation of arrOfObj with the template was broken(don't know why) to fix this and a little bit improved version of the combiner see below
// use const when possible
const arr1 = [];
const arr2 = [];
// was fine as is
// Getting the Keys and values of input in 2 separate arrays
inputs.forEach(x => {
for (const key in x) {
arr1.push(key);
arr2.push(x[key]);
}
});
//template made some problems... don't know why
// using arrOfObj to have template dubplicated the same number of arr1 and arr2
const arrOfObj = inputs.map(() => {
return {
labels: [],
values: []
}
})
// function declaration for combiner
function combineArr(target, ...arrays) {
target.map((ele, superIndex) => {
Object.keys(ele).map((key, index) => {
target[superIndex][key].push(arrays[index][superIndex])
})
})
return target;
}
// calling for new array
console.log(combineArr(arrOfObj, arr1, arr2))

Accessing a JSON integer element

I have an JSON-Object as follows:
Input for the months is
customerSend,customerReceived,totalSendAllCustomers,totalReceivedAllCustomers
var emailObj = {
"kundenNummer":17889,
"jahre":
{
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
How exactly do I access the specific year?
I already tried it like this:
var keysYears = Object.keys(emailObj.jahre);
var currentSelectedYear = keysYears[0];
var keysMonth = Object.keys(emailObj.jahre[currentSelectedYear]);
var currentSelectedMonth = keysMonth[0];
document.write(emailObj.jahre[currentSelectedYear].2017[0]);
I also tried some other ways of doing this but I already deleted those.
Can you tell me how to access the 2017 or 2018 data?
I know that I could convert them into strings but I want to know if I could also do it this way.
You can call the properties of your object emailObj by their names.
Either with a dot notation
emailObj.kundenNummer
Or by brackets notation
emailObj["kundenNummer"]
The dot notation won't work in your case because the name of your property is a number. You should then use
emailObj.jahre["2017"]
var emailObj = {
"kundenNummer": 17889,
"jahre": {
"2017": {
"Januar": [15, 30, 75, 125],
"Februar": [17, 32, 77, 127],
"März": [19, 34, 79, 129],
},
"2018": {
"Januar": [28, 12, 66, 198],
"Oktober": [40, 4, 40, 5],
}
}
};
let year = "2017";
let month = "Januar";
console.log(emailObj.jahre[year][month]);
You should use bracket notation.
document.write(emailObj.jahre[currentSelectedYear][currentSelectedMonth][0]);
var emailObj = {
"kundenNummer":17889,
"jahre":
{
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
var keysYears = Object.keys(emailObj.jahre);
var currentSelectedYear = keysYears[0];
var keysMonth = Object.keys(emailObj.jahre[currentSelectedYear]);
var currentSelectedMonth = keysMonth[0];
document.write(emailObj.jahre[currentSelectedYear][currentSelectedMonth][0]);
In a JavaScript object, the key is always a string, even if you use an integer it will be converted into a string.
obj = {
key1: //contents
key2: //contents
}
To access a specific key:
obj.key1
obj['key1']
For your example:
emailObj.jahre['2017']
emailObj['jahre']['2017']
Use the for in looping construct to loop through the keys of an object:
var emailObj = {
"kundenNummer":17889,
"jahre": {
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
for (key in emailObj.jahre) {
console.log(emailObj.jahre[key]) //Here key will be '2017', '2018' etc
}
You cannot access with dot notation properties which contain as name a number in JavaScript. Instead you should consider using bracket notation.
Example:
emailObj.jahre['2017']
var emailObj = {
"kundenNummer": 17889,
"jahre": {
2017: {
"Januar": [15, 30, 75, 125],
"Februar": [17, 32, 77, 127],
"März": [19, 34, 79, 129],
},
2018: {
"Januar": [28, 12, 66, 198],
"Oktober": [40, 4, 40, 5],
}
}
};
console.log(emailObj['jahre']['2017']);
console.log(emailObj.jahre['2017']);

Categories

Resources