I am creating a dashboard to show data from various stats. I am using Django and Google Charts for creating graphs. It has been good so far but I am stuck at one particular case.
the model class is-
class Registration(models.Model):
event_type = models.CharField(max_length=80)
date_time = models.DateField()
count = models.IntegerField()
my query is-
Registration.objects.filter(event_type__in=['VHRAssmntCompleted',
'VNAAssmntCompleted',
'NonsmokersDeclrtn',
'MWBAssmntCompleted',
'VHCAssmntCompleted',
'SV Document Uploads',
'PapSmear',
'Mammogram',],
date_time__range=(d3,d1)).order_by('date_time')
I get the data in following format:
[["VHR", "2019-02-1", 23],
["VNA", "2019-02-1", 34],
["PAP", "2019-02-1", 50],
["VHR", "2019-02-2", 92],
["VNA", "2019-02-2", 13],
["PAP", "2019-02-2", 65],
["VHR", "2019-02-3", 192],
["VNA", "2019-02-3", 43],
["PAP", "2019-02-3", 11]]
To create a Combo Chart in need the data in following format(something like python dataframe):
[["date", "VHR", "VNA", "PAP" ],
["2019-02-1", 23,34,50],
["2019-02-2", 92,13,65],
["2019-02-3", 192,43,11]]
I am unable to find a way to do this, either format it using Django ORM query itself or transform using JS.
I need help with what approach should I go.
You could take an object for the column position of the values and collect the data by date and column.
var data = [["VHR", "2019-02-1", 23], ["VNA", "2019-02-1", 34], ["PAP", "2019-02-1", 50], ["VHR", "2019-02-2", 92], ["VNA", "2019-02-2", 13], ["PAP", "2019-02-2", 65], ["VHR", "2019-02-3", 192], ["VNA", "2019-02-3", 43], ["PAP", "2019-02-3", 11]],
cols = { VHR: 1, VNA: 2, PAP: 3 },
result = data.reduce((r, [key, date, value]) => {
var row = r.find(([d]) => d === date);
if (!row) {
r.push(row = [date, 0, 0, 0]);
}
row[cols[key]] = value;
return r;
}, [["date", "VHR", "VNA", "PAP"]]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
you can group them by date, then using Object.entries loop through the grouped result and transform it to [date, "VHR", "VNA", "PAP"]
const data = [
["VHR", "2019-02-1", 23],
["VNA", "2019-02-1", 34],
["PAP", "2019-02-1", 50],
["VHR", "2019-02-2", 92],
["VNA", "2019-02-2", 13],
["PAP", "2019-02-2", 65],
["VHR", "2019-02-3", 192],
["VNA", "2019-02-3", 43],
["PAP", "2019-02-3", 11]
];
const grouped = data.reduce((all, [key, date, value]) => {
all[date] = {
...all[date],
[key]: value
};
return all;
}, {});
const result = ["date", "VHR", "VNA", "PAP"].concat(
Object.entries(grouped).map(([date, obj]) => [date, ...Object.values(obj)])
);
console.log(result);
Related
Question - How can mapp json data into objects? json can be found here
Details - I was able to connect to api and get the json data. Now i want to map 2 array data.hourly.time[] & data.hourly.temperature_2m[] into 1 DataTable? I need datatable inorder to display json data into google charts api Below is a example of datable format that I am trying to create using 2 arrays from json data
javascript code
$.ajax({
url: FullURL,
dataType: "JSON",
success: function (data) {
alert(data.hourly.time[1] + "_" + data.hourly.temperature_2m[1]);
// This ex below is from google api document. i want to conver json array into datatable
var data = new google.visualization.DataTable();
data.addColumn('number', 'X');
data.addColumn('number', 'Dogs');
data.addRows([
[0, 0], [1, 10], [2, 23], [3, 17], [4, 18], [5, 9],
[6, 11], [7, 27], [8, 33], [9, 40], [10, 32], [11, 35],
[12, 30], [13, 40], [14, 42], [15, 47], [16, 44], [17, 48],
[18, 52], [19, 54], [20, 42], [21, 55], [22, 56], [23, 57],
[24, 60], [25, 50], [26, 52], [27, 51], [28, 49], [29, 53]
]);
You can use the Array.prototype.forEach() method for this. Array.prototype.forEach() mdn.
The second argument of the callback is the current index of the element. And you can use this index for another array.
const data = new google.visualization.DataTable();
data.addColumn('number', 'Time');
data.addColumn('number', 'Temperature');
const temperatureArray = data.hourly.temperature_2m;
data.hourly.time.forEach((currentTime, index)=>{
data.addRow([currentTime, temperatureArray[index]])
});
Here is how you can convert the JSON input into a grid (array of arrays) using a array .map(), for use in a datatable with data.addRows(grid). The JSON input is reduced to 10 items for this demo:
const input = { "latitude": 42.635273, "longitude": -73.72632, "generationtime_ms": 7.2220563888549805, "utc_offset_seconds": -18000, "timezone": "America/New_York", "timezone_abbreviation": "EST", "elevation": 35.0, "current_weather": { "temperature": -0.1, "windspeed": 3.2, "winddirection": 304.0, "weathercode": 3, "time": "2023-01-21T18:00" }, "hourly_units": { "time": "iso8601", "temperature_2m": "°C" }, "hourly": { "time": ["2023-01-14T00:00", "2023-01-14T01:00", "2023-01-14T02:00", "2023-01-14T03:00", "2023-01-14T04:00", "2023-01-14T05:00", "2023-01-14T06:00", "2023-01-14T07:00", "2023-01-14T08:00", "2023-01-14T09:00"], "temperature_2m": [-0.5, -0.8, -1.2, -1.6, -1.7, -1.6, -1.6, -1.7, -2.1, -2.2] } };
let grid = input.hourly.time.map((val, idx) => [val, input.hourly.temperature_2m[idx]]);
console.log('grid:', grid);
// data.addRows(grid);
Output:
grid: [
[ "2023-01-14T00:00", -0.5 ],
[ "2023-01-14T01:00", -0.8 ],
[ "2023-01-14T02:00", -1.2 ],
[ "2023-01-14T03:00", -1.6 ],
[ "2023-01-14T04:00", -1.7 ],
[ "2023-01-14T05:00", -1.6 ],
[ "2023-01-14T06:00", -1.6 ],
[ "2023-01-14T07:00", -1.7 ],
[ "2023-01-14T08:00", -2.1 ],
[ "2023-01-14T09:00", -2.2 ]
]
It might take some time to get used to functional programming, in this example using .map() instead of a for loop, but functional programming makes code more efficient and readable.
References:
.map(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Functional programming overview: https://www.freecodecamp.org/news/functional-programming-in-javascript/
I have a Record<string, number[][]> and trying to perform calculation over these values.
An example input:
const input1 = {
key1: [
[2002, 10],
[2003, 50],
],
};
const input2 = {
key1: [
[2002, 20],
[2003, 70],
],
};
const input3 = {
key1: [
[2002, 5],
[2003, 60],
],
};
For each key, and for the specific year I want to do the following
year => input1 + input2 - input3
// output: 2002 => 25, 2003 => 60
For this I have been playing around with lodash/fp.
map(a => a.map(nth(1)))(map('key1')([input1, input2]))
// [[10, 50], [20, 70], [5, 60]]
is there a way to somehow pass the inputs and iterate over them, and somehow get a callback function to get the values to perform the calculation.
I tried zip, zipWith but didn’t get to anywhere with them.
Any clue what I can do in this instance?
Thank you for your help.
You could get an object with _.fromPairs method and then use _.mergeWith to add and subtract.
const input1 = {
key1: [
[2002, 10],
[2003, 50],
],
};
const input2 = {
key1: [
[2002, 20],
[2003, 70],
],
};
const input3 = {
key1: [
[2002, 5],
[2003, 60],
],
};
const [one, two, three] = _.map([input1, input2, input3], ({key1 }) => _.fromPairs(key1))
const add = _.mergeWith(one, two, (a, b) => a + b)
const sub = _.mergeWith(add, three, (a, b) => a - b)
console.log(add)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
function openOrSenior(data) {
let newArr = []
let userData = [data]
return userData.forEach(data => {
data.map(data => {
let answer = (data[0] >= 55 && data[1] > 7) ? console.log("Senior") : console.log("Open");
return answer
})
})
}
the above function should either display senior or open in this form [ 'Open', 'Senior', 'Open', 'Senior' ]
the outpout i got instead was:
Open
Senior
Open
Senior
an example of what is expected:
input = [[18, 20], [45, 2], [61, 12], [37, 6], [21, 21], [78, 9]]
output = ["Open", "Open", "Senior", "Open", "Open", "Senior"]
You could just map the wanted strings, depending on the values of left an right items of the nested arrays.
function openOrSenior(data) {
return data.map(([l, r]) => l >= 55 && r > 7 ? 'Senior' : 'Open');
}
const data = [[18, 20], [45, 2], [61, 12], [37, 6], [21, 21], [78, 9]];
console.log(openOrSenior(data)); // ["Open", "Open", "Senior", "Open", "Open", "Senior"]
If you would like to use the same code-template that you have provided in the description then you can solve it in this way:
function openOrSenior(data) {
let newArr = []
let userData = [data]
userData.forEach(data => {
data.map(dataInside => {
let answer = (dataInside[0] >= 55 && dataInside[1] > 7) ? "Senior" : "Open"
newArr.push(answer)
})
})
return newArr
}
const data = [[18, 20], [45, 2], [61, 12], [37, 6], [21, 21], [78, 9]];
console.log(openOrSenior(data)); // ["Open", "Open", "Senior", "Open", "Open", "Senior"]
But in above solution, we are performing redundant operations, the better way to solve it will be this one:
function openOrSenior(data) {
return data.map(dataInside => (dataInside[0] >= 55 && dataInside[1] > 7) ? "Senior" : "Open")
}
const data = [[18, 20], [45, 2], [61, 12], [37, 6], [21, 21], [78, 9]];
console.log(openOrSenior(data)); // ["Open", "Open", "Senior", "Open", "Open", "Senior"]
I am trying to create GroubBy for an array as shown below:
const sqlData = [
['a','b','c','d','e','f','g'],
['aa','10/11/2020','##',2,3,4,5],
['bb','10/12/2020','$$',23,13,14,95],
['aa','10/12/2020','!!',21,23,24,58],
['aa','10/12/2020','##',22,13,44,55],
['aa','10/11/2020','$$',12,33,45,52],
['bb','10/11/2020','!!',52,32,49,52],
['aa','10/11/2020','!!',72,53,44,51],
['bb','10/11/2020','##',12,3,43,65],
['bb','10/13/2020','##',112,63,4,85],
['bb','10/13/2020','$$',22,63,24,95],
['bb','10/12/2020','$$',32,73,44,50],
['bb','10/13/2020','$$',52,388,24,50],
['bb','10/13/2020','$$',72,233,41,52],
['bb','10/13/2020','##',82,123,46,57],
['bb','10/13/2020','!!',92,763,47,58],
];
So I have my sqlData[0] as Table Header and rest of the data as Table Body. I am interested in grouping by based on the third input which is groupby headers as shown below:
groupbyheaders = [a,b,c]
in a function GenerateGroupByData(CurrentData,groupbyheaders,ops = sum). Where I have choices such as sum, average, max, min, etc.
How I procceed to solve this problem is through this:
First Create Header Array which is All Headers - Non Numeric Headers (where mathematical Op is not possible) + GroupByHeaders (that are Non Numeric)
So in this case, it gives me:
[a,b,c (which are groupby headers + non Numeric), d, e, f, g]
Now I am performing a concatenation operation after looping through the entire initial data, which looks like this:
aa<some unique token>10/11/2020<some unique token>##
Now from all these values, I am generating unique entries and then based on those entries I am performing summation. Though I have done the ideation, scripting this is actually getting bigger and bigger and so I wanted all of yours inputs to do this task in a simplified and computation friendly way. THanks!
Consider the following. It uses ES6 syntax, but can easily be modified if you'd like.
const sqlData = [
['a', 'b', 'c', 'd', 'e', 'f', 'g'],
['aa', '10/11/2020', '##', 2, 3, 4, 5],
['bb', '10/12/2020', '$$', 23, 13, 14, 95],
['aa', '10/12/2020', '!!', 21, 23, 24, 58],
['aa', '10/12/2020', '##', 22, 13, 44, 55],
['aa', '10/11/2020', '$$', 12, 33, 45, 52],
['bb', '10/11/2020', '!!', 52, 32, 49, 52],
['aa', '10/11/2020', '!!', 72, 53, 44, 51],
['bb', '10/11/2020', '##', 12, 3, 43, 65],
['bb', '10/13/2020', '##', 112, 63, 4, 85],
['bb', '10/13/2020', '$$', 22, 63, 24, 95],
['bb', '10/12/2020', '$$', 32, 73, 44, 50],
['bb', '10/13/2020', '$$', 52, 388, 24, 50],
['bb', '10/13/2020', '$$', 72, 233, 41, 52],
['bb', '10/13/2020', '##', 82, 123, 46, 57],
['bb', '10/13/2020', '!!', 92, 763, 47, 58],
];
// First, change the data to be an array of objects [{ column: value }]
const allColumns = sqlData.splice(0, 1)[0];
const sqlDataObjects = sqlData.map(row => {
const result = {};
allColumns.forEach(c => result[c] = row[allColumns.indexOf(c)]);
return result;
});
// Dynamically create a mapping of method names to functions.
// All deal with an array and should return a single number.
const methods = {
sum: (arr) => arr.reduce((total, v) => total + v, 0),
avg: (arr) => methods.sum(arr) / arr.length,
};
function groupBy(columns, methodName) {
const keyFn = (obj) => columns.map(c => obj[c]).join('<some unique token>');
const result = {};
// Create an object that just stores all raw values, grouped in arrays
// (like ARRAY_AGG in PostgreSQL)
const groups = sqlDataObjects.reduce((obj, row) => {
// Construct a key and select all columns that we need to store in the result
const key = keyFn(row);
const aggregatedColumnKeys = allColumns.filter(k => !columns.includes(k));
if (!(key in obj)) {
obj[key] = {};
aggregatedColumnKeys.forEach(c => obj[key][c] = []);
}
aggregatedColumnKeys.forEach(c => obj[key][c].push(row[c]));
return obj;
}, {});
// Choose the method to apply
const method = methods[methodName];
// Apply the method to each column in each group
Object.keys(groups).forEach(groupName => {
const group = groups[groupName];
result[groupName] = {};
Object.keys(group).forEach(c => {
result[groupName][c] = method(group[c]);
});
});
return result;
}
console.log(groupBy(['a', 'b', 'c'], 'sum'));
console.log(groupBy(['a', 'b', 'c'], 'avg'));
I have two arrays of array in Javascript like
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]];
var array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]];
I want to get the set of arrays from array1 which match the first element of each array of the array2
in my example case both array1 and array2 have array with first element as 10 11 and 12, so it should return
[[10, 2], [11, 4], [12, 30]];
is there any easy and efficient way using pure javscript or lodash, underscor framework or something like that. Without iterate over and match one by one of this two array ?
In ES6, you could use Set.
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
set = new Set(array2.map(a => a[0])),
result = array1.filter(a => set.has(a[0]));
console.log(result);
Version with an object as hash table
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
result = array1.filter(function (a) {
return this[a[0]];
}, array2.reduce(function (r, a) {
r[a[0]] = true;
return r;
}, Object.create(null)));
console.log(result);
You can use lodash _.intersectWith function in order to solve this problem in an inline.
_.intersectionWith(array1, array2, function(a, b) {
return a[0] === b[0];
});
I don't know about performance cos I haven't had the chance to have a look at the source code of this function. Anyway, I like it for its simplicity. Here's the fiddle in case you want to check it out.
If you can make use of Set, then you can compute the a set of numbers to look for first and use .filter to only get the arrays whose first element is in that set:
var haystack = new Set(array2.map(x => x[0]));
var newArray = array1.filter(x => haystack.has(x[0]));
Of course you can also use the lodash or underscore versions of .map and .filter.
Alternatives to using Set would be:
Create an array of numbers instead and use indexOf to test existence. That will scale linearly with the number of elements:
var haystack = array2.map(x => x[0]);
var newArray = array1.filter(x => haystack.indexOf(x[0]) > -1);
Create an object with number -> true entries to test existence with in, hasOwnProperty or just object access:
var haystack = array2.reduce((obj, x) => (obj[x[0]] = true, obj), {});
var newArray = array1.filter(x => haystack[x[0]]);
Which one performs better depends on the number of elements you have and the environment the code is running in.
You can do this with filter() and find()
var array1 = [
[10, 2],
[11, 4],
[12, 30],
[13, 17],
[14, 28]
];
var array2 = [
[8, 13],
[9, 19],
[10, 6],
[11, 7],
[12, 1]
];
var result = array1.filter(function(ar) {
return array2.find(function(e) {
return e[0] == ar[0]
})
})
console.log(result)
I would do this with a Map anf filter combo in ES6 as follows;
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
m = new Map(array2),
array3 = array1.filter(a => m.has(a[0]));
console.log(array3);
If you need backwards compatibility other answers are pretty good.