js: array with in an associative array - javascript

I am trying to set up an associative array for the following data:
name date alpha beta
Andrew 12/08/07 2.3 1.4
5/12/07
26/03/08
____________________________________
Fred 3/09/07 2.1 1.1
23/01/08
____________________________________
Basically, each patient would have a name and alpha , beta value but multiple dates on which they visited doctor. I was thinking of something like following where name is the primary key and dates are stored in an array and alpha, beta is a float value associated with the key.
var info = [{ name: [{ dates: [ ], alpha: float, beta: float }] }];
and then this info array would be populated on reading the csv file. What would be the right format for initialising such an associative array? or what other data structure would be a good approach for representing such a data?
Thanks in advance!

Edit: Since each patient has a unique name, instead of using an array, you should consider using a single object where each patient is an object identified by the object key, for example:
var patientList = {
andy: {},
bob: {}
};
To get your data from your CSV file into this structure you might consider something like this:
var csv = 'Andrew\t12/08/07\t1.2\t3.4\nAndrew\t15/09/08\t1.2\t3.4\nAndrew\t14/08/07\t1.2\t3.4\t\nBob\t18/09/08\t1.2\t3.4\nAndrew\t21/08/07\t1.2\t3.4\nDavid\t31/09/08\t1.2\t3.4\nAndrew\t22/08/07\t1.2\t3.4\t\nSam\t26/09/08\t1.2\t3.4';
// Split the CSV file at the carriage return.
var data = csv.split('\n');
// Recursive over the data with `map`, splitting each line up
// on the tabs and returning a patient object for each.
data = data.map(function (el) {
var patient = el.split('\t');
return {
name: patient[0],
date: patient[1],
alpha: patient[2],
beta: patient[3]
}
});
function getListOfPatientNames(arr) {
var newarr = [];
// For each patient object return the patient name only
newarr = arr.map(function (patient) {
return patient.name;
});
// A quick way of filtering out duplicates
return newarr.filter(function(elem, pos) {
return newarr.indexOf(elem) == pos;
});
}
// Return a unique list of names, and sort them.
var names = getListOfPatientNames(data).sort();
var patientList = {};
for (var i = 0, l = data.length; i < l; i++) {
var name = data[i].name;
// If the patient name doesn't exist in patientList yet
if (!patientList[name]) {
// Add a new patient object using the name as the key
var newPatient = {
dates: [data[i].date],
alpha: data[i].alpha,
beta: data[i].beta
};
patientList[name] = newPatient;
} else {
// If the patient already exists push the date to the dates array
patientList[name].dates.push(data[i].date);
}
}
Demo

The term "associative array" is almost never used wrt JavaScript; you use objects (sometimes called "maps" or "dictionaries") for name/value information, or arrays for ordered data.
It looks like you want an array of patient objects, like this:
var patients = [
{
name: "Andrew",
dates: [/*...the dates...*/],
alpha: 2.3,
beta: 1.4
},
{
name: "Fred",
dates: [/*...the dates...*/],
alpha: 2.1,
beta: 1.1
}
];
You might or might not want to use a constructor function to create those objects, depending on your needs, but with the simple data you've given there's probably no need.

Related

Mapping data with dynamic variables

I am having a little trouble trying to achieve something. So I have some data
let data = [
{
"ID": 123456,
"Date": "2012-01-01",
"Irrelevant_Column_1": 123,
"Irrelevant_Column_2": 234,
"Irrelevant_Column_3": 345,
"Irrelevant_Column_4": 456
},
...
]
And I wanted to remove the irrelevant columns. So someone suggested using map
data = data.map(element => ({ID: element.ID, Date: element.Date}))
The problem is, I dont want to define the columns. I have the user select the columns to keep, and assign them to a variable. I can then do something like
let selectedId = this.selectedIdCol;
The issue is, I am unable to now use this within the map. I am trying
let selectedId = this.selectedIdCol;
this.parsed_csv = data.map(element => (
{ID: element.selectedId, Date: element.Date}
));
But that does not seem to work, just returns the date. Also, my IDE is saying that the variable is unused. So how can I use the selectedId variable as part of the map function?
Thanks
You can do using Bracket notation notation and helper function
Whenever you want to use variable to access property you need to use [] notation.
let data = [{"ID": 123456,"Date": "2012-01-01","column_1": 123,"column_2": 234,"column_3": 345,"column_4": 456},{"ID": 123456,"Date": "2018-10-01", "column_1": 123,"column_2": 234,"column_3": 345,"column_4": 46},]
function selectDesired(data,propName1,propName2){
return data.map(e=> ({[propName1]: e[propName1], [propName2]: e[propName2]}))
}
console.log(selectDesired(data, 'Date', 'column_4'))
The basic technique is illustrated here, assuming that the user's selected column_name is "ID"
let data = [
{
"ID": 123456,
"Date": "2012-01-01",
"Irrelevant_Column_1": 123,
"Irrelevant_Column_2": 234,
"Irrelevant_Column_3": 345,
"Irrelevant_Column_4": 456
}
];
let column_name = "ID";
let curated = data.map(element=>({[column_name]: element[column_name]}));
console.log(curated)
If you are wanting the user to be able to multi-select their columns,(assuming data from above is still in scope)
let user_selection = ["ID","Date"];
let curated = data.map(
(element)=>
{
let item = {};
user_selection.forEach(
(property)=>
{
item[property] = element[property];
}
return item;
}
);
To set up a function that can handle multiple calling situations without having a monstrously hack-and-patched source history, set up the function's signature to receive a spread list of properties.
If you wish to extend the capabilities to accept
a csv property list
an array of property names delivered directly
an array of property names
you can assume the properties argument in the signature to be an iterable of property groupings, having the most basic grouping be a singleton.
Commentary embedded within the sample code to expound in more detail
var getProjection = (data,...properties) =>
{
//+=================================================+
// Initialize the projection which will be returned
//+=================================================+
let projection = {};
//+=================================================+
// Set up the property mapping func
//+=================================================+
let safe_assign = (source, target ,propertyDesignator)=>
{
if(source[propertyDesignator])
{
target[propertyDesignator] = source[propertyDesignator];
}
};
//+=====================================================+
// Iterate the properties list, assuming each element to
// be a property grouping
//+=====================================================+
properties.forEach(
(propertyGroup)=>
{
//+-----------------------------------------------+
// If the propertyGroup is not an array, perform
// direct assignment
//+-----------------------------------------------+
if(!Array.isArray(propertyGroup))
{
//+-------------------------------------------+
//Only map the requested property if it exists
//+-------------------------------------------+
safe_assign(data,projection,propertyGroup);
}
//+-----------------------------------------------+
// If the propertyGroup *is* an array, iterate it
// This technique obviously assumes that your
// property groupings are only allowed to be one
// level deep. This is for accommodating distinct
// calling conventions, not for supporting a deeply
// nested object graph. For a deeper object graph,
// the technique would largely be the same, but
// you would need to recurse.
//+-----------------------------------------------+
if( Array.isArray(propertyGroup))
{
propertyGroup.forEach(
(property)=>
{
safe_assign(data,projection,property);
}
}
}
);
//+===================================+
// Return your projection
//+===================================+
return projection;
};
//+--------------------------------------+
//Now let's test
//+--------------------------------------+
let data = [
{ID:1,Foo:"Foo1",Bar:"Bar1",Baz:"Inga"},
{ID:2,Foo:"Foo2",Bar:"Bar2",Baz:"Ooka"},
{ID:3,Foo:"Foo3",Bar:"Bar3",Baz:"oinga",Floppy:"Floop"},
{ID:4,Foo:"Foo4",Good:"Boi",Bar:"Bar3"Baz:"Baz"}
];
//***************************************
//tests
//***************************************
var projection1 = getProjection(data.find(first=>first),"ID","Baz"));//=>{ID:1,Baz:"Inga"}
var projection2 = getProjection(data[0],["ID","Baz"]);//=>{ID:1,Baz:"Inga"}
var projection3 = getProjection(data[0],...["ID","Baz"]);//=>{ID:1,Baz:"Inga"}
var user_selected_properties = ["ID","Good","Baz"];
var projections = data.map(element=>getProjection(element,user_selected_properties));
//+=====================================+
// projections =
// [
// {ID:1,Baz:"Inga"},
// {ID:2,Baz:"Ooka"},
// {ID:3,Baz:"oinga"},
// {ID:4,Good:"Boi",Baz:"Baz"}
// ];
//+=====================================+

javascript sort key value pair on frequency in different array

I would like to store product information in a key, value array, with the key being the unique product url. Then I would also like to store the visit frequency of each of these products. I will store these objects as window.localStorage items, but that's not very important.
The thing I had in mind was two key value arrays:
//product information
prods["url"] = ["name:product_x,type:category_x,price:50"]
//product visits frequency
freq["url"] = [6]
Then I would like to sort these prods based on the frequency.
Is that possible?
Hope you guys can help! Thanks a lot
Well you seem to have made several strange choices for your data format/structure. But assuming the format of the "prod" is beyond your control but you can choose your data structure, here's one way to do it.
Rather than two objects both using url as a key and having one value field each I've made a single object still keyed on url but with the product and frequency information from each in a field.
Objects don't have any inherent order so rather than sorting the table object I sort the keys, your "url"s ordered by ascending frequency.
To show that it's sorted that way I print it out (not in the same format).
For descending frequency, change data[a].freq - data[b].freq to data[b].freq - data[a].freq
var data = {
"url": {
prod: "name:product_x,type:category_x,price:50",
freq: 6
},
"url2": {
prod: "name:product_y,type:category_y,price:25",
freq: 3
}
};
var sorted = Object.keys(data).sort((a, b) => data[a].freq - data[b].freq);
console.log(sorted.map(k => [data[k].freq, k, data[k].prod]));
There's more than one way to format the data, which would change the shape of the code here.
maybe something like this:
var prods = [
{url:1, val:[{name:'a',type:'x',price:60}]},
{url:2, val:[{name:'b',type:'x',price:30}]},
{url:3, val:[{name:'c',type:'x',price:50}]},
{url:4, val:[{name:'c',type:'x',price:20}]},
{url:5, val:[{name:'c',type:'x',price:10}]},
{url:6, val:[{name:'c',type:'x',price:40}]}
];
var freq = [
{url:1, freq:6},
{url:2, freq:3},
{url:3, freq:5},
{url:4, freq:2},
{url:5, freq:1},
{url:6, freq:4}
];
prods.sort(function (a, b) {
var aU = freq.filter(function(obj) {
return obj.url === a.url;
});
var bU = freq.filter(function(obj) {
return obj.url === b.url;
});
if (aU[0].freq > bU[0].freq) {
return 1;
}
if (aU[0].freq < bU[0].freq) {
return -1;
}
return 0;
});

Summarize & Group By with Lodash

I'm new to Lodash and I'm trying to perform a complex sum with group by as SQL but I don't find any solution. I have tried to use/combine multiple Lodash functions without success.
My requirement is like this. I have a JSON response:
input =
[{"quantity":1067,"gross_revenue":4094.2,"date":"03","company":"Cat1","product":"Car"},
{"quantity":106,"gross_revenue":409,"date":"02","company":"Cat2","product":"Car"},
{"quantity":106,"gross_revenue":85,"date":"03","company":"Cat2","product":"House"},
{"quantity":106,"gross_revenue":100,"date":"02","company":"Cat3","product":"House"},
{"quantity":20,"gross_revenue":150,"date":"03","company":"Cat5","product":"Technology"},
{"quantity":40,"gross_revenue":100,"date":"01","company":"Cat5","product":"Technology"},
{"quantity":20,"gross_revenue":15,"date":"01","company":"Cat5","product":"Car"},
{"quantity":20,"gross_revenue":18,"date":"01","company":"Cat5","product":"House"},
{"quantity":20,"gross_revenue":2,"date":"01","company":"Cat2","product":"House"},
{"quantity":20,"gross_revenue":25,"date":"01","company":"Cat3","product":"House"}]
I need to generate a result as below to populate the series for a HighChart:
[{ name: 'Car', data: [15, 409, 4094.2] },
{ name: 'House', data:[45, 100, 85] },
{ name: 'Techonology', data:[100, null, 150] }]
Those values are the result from:
Make a group by using Product with the tag name
Based on following procedure, generate an array with the tag data
2.1 Sum the gross revenue based on Product and date (all existing dates)
2.2 Include a null value if there doesn't exist gross revenue for any existing day
2.3 Sort the results for gross revenue based on date, ascending order
Is this possible? Or is there another solution for this?
Thanks.
Here's one way to do it - certainly not the only solution...
var input = [
{"quantity":1067,"gross_revenue":4094.2,"date":"03","company":"Cat1","product":"Car"},
{"quantity":106,"gross_revenue":409,"date":"02","company":"Cat2","product":"Car"},
{"quantity":106,"gross_revenue":85,"date":"03","company":"Cat2","product":"House"},
{"quantity":106,"gross_revenue":100,"date":"02","company":"Cat3","product":"House"},
{"quantity":20,"gross_revenue":150,"date":"03","company":"Cat5","product":"Technology"},
{"quantity":40,"gross_revenue":100,"date":"01","company":"Cat5","product":"Technology"},
{"quantity":20,"gross_revenue":15,"date":"01","company":"Cat5","product":"Car"},
{"quantity":20,"gross_revenue":18,"date":"01","company":"Cat5","product":"House"},
{"quantity":20,"gross_revenue":2,"date":"01","company":"Cat2","product":"House"},
{"quantity":20,"gross_revenue":25,"date":"01","company":"Cat3","product":"House"}
];
var result = [];
var groupedByProduct = _.groupBy(input, "product");
// get the set of unique dates
var dates = _.uniq(_.map(input, 'date'));
// for each product, perform the aggregation
_.forEach(groupedByProduct, function(value, key) {
// initialize the data array for each date
data = [];
for (var i = 0; i < dates.length; i++) {
data.push(null);
}
// aggregate gross_revenue by date
_.forEachRight(_.groupBy(groupedByProduct[key], "date"), function(dateValue, dateKey) {
// use the date as an array index
data[parseInt(dateKey) - 1] = _.sumBy(dateValue, function(o) {
return o.gross_revenue
});
});
// push into the result array
result.push({"name": key, "data": data});
});
document.getElementById("result").innerHTML = JSON.stringify(result);
<script src="https://cdn.jsdelivr.net/lodash/4.11.1/lodash.min.js"></script>
<pre id="result"></pre>

Best way to store this data (array, object, etc)

I need to store (many) objects or arrays of data, which need to have the following criteria:
I need to be able to add a new set of data into the existing data easily
I need to be able to sort the data by date/ time added easily (array in order of when entries were pushed to it)
I need to be able to grab an entry easily using a reference, either integer or string. This is important, at the moment I have to do an $.each() to loop through my data until I find the entry I want.
I have tried using a structure like:
saved_info = {
1001: {//all my data for ref 1001},
1002: {//all my data for ref 1002}
}
which gave me what wanted of being able to grab the info easily given a reference:
info = saved_info[1001];
however, the reference numbers I use aren't in order - I use a reference given to me (its a unique identifier), therefore the object isn't in order of when items were added/saved/pushed.
You can use two objects:
One that stores the data by key
Another that stores the sort order
This way you can (i) lookup an element by key (ii) loop over elements in the order they were inserted. Rough outline of the structure:
var DataObject = {
data: {},
sort: []
};
Here is how you add data to this structure:
DataObject.data[1004] = {name: "Test 4"};
DataObject.sort.push(1004);
DataObject.data[1001] = {name: "Test 1"};
DataObject.sort.push(1001);
DataObject.data[1003] = {name: "Test 3"};
DataObject.sort.push(1003);
DataObject.data[1002] = {name: "Test 2"};
DataObject.sort.push(1002);
Here is how you perform a random access:
console.log(DataObject.data[1001].name);
console.log(DataObject.data[1003].name);
And here is how you iterate over all elements in the order they were added:
var i;
for (i = 0; i < DataObject.sort.length; i++) {
console.log(DataObject.data[DataObject.sort[i]].name);
}
It is possible to wrap the entire logic inside a class:
function DataObject() {
this.data = {};
this.sort = [];
this.setItem = function (k, v) {
this.data[k] = v;
this.sort.push(k);
};
this.getItemByKey = function (k) {
return this.data[k];
};
this.getItemByPos = function (i) {
return this.data[this.sort[i]];
};
this.getAllItems = function () {
var i, r = [];
for (i = 0; i < this.sort.length; i++) {
r.push(this.data[this.sort[i]]);
}
return r;
};
}
var t = new DataObject();
t.setItem(1001, {name: "Test 1"});
t.setItem(1002, {name: "Test 2"});
t.setItem(1003, {name: "Test 3"});
t.setItem(1004, {name: "Test 4"});
console.log(t.getItemByKey(1001));
console.log(t.getItemByPos(0));
console.log(t.getAllItems());
Try to build a Json like this,
var xJson = {
"1001":{//all my data for ref 1001},
"1002":{//all my data for ref 1002}
};
and you can fetch the records as per your wish using the bracket notation, since we are using a numeric value as a key.
var xData = xJson["1001"];

Hash Tables in javascript

I am trying to build a data structure.
In my limited knowledge, 'hash table' seems to be the way to go. If you think there is an easier way, please suggest it.
I have two, 1-dimensional arrays:-
A[] - contains names of badges (accomplishment)
B[] - contains respective dates those achievements were accomplished from array A[].
An achievement/accomplishment/badge can be accomplished more than one time.
Therefore a sample of the two arrays:-
A['scholar', 'contributor', 'teacher', 'student', 'tumbleweed', 'scholar'.....,'scholar',......]
B['1/2010', '2/2011', '3/2011', '6/2012', '10/2012', '2/2013',......'3/2013',........]
What I want to achieve with my data structure is:-
A list of unique keys (eq:- 'scholar') and all of its existing values (dates in array B[]).
Therefore my final result should be like:-
({'scholar': '1/2010', '2/2013', '3/2013'}), ({'contributor' : ........})..........
This way I can pick out a unique key and then traverse through all its unique values and then use them to plot on x-y grid. (y axis labels being unique badge names, and x axis being dates, sort of a timeline.)
Can anyone guide me how to build such a data structure??
and how do I access the keys from the data structure created.... granted that I don't know how many keys there are and what are their individual values. Assigning of these keys are dynamic, so the number and their names vary.
Your final object structure would look like this:
{
'scholar': [],
'contributor': []
}
To build this, iterate through the names array and build the final result as you go: if the final result contains the key, push the corresponding date on to its value otherwise set a new key to an array containing its corresponding date.
something like:
var resultVal = {};
for(var i = 0; i < names.length; ++i) {
if(resultVal[names[i]]) {
resultVal[names[i]].push(dates[i]);
} else {
resultVal[names[i]] = [dates[i]];
}
}
Accessing the result - iterating through all values:
for(var key in resultVal) {
var dates = resultVal[key];
for(var i = 0; i < dates.length; ++i) {
// you logic here for each date
console.log("resultVal[" + key + "] ==> " + resultVal[key][i]);
}
}
will give results like:
resultVal[scholar] ==> 1/2010
resultVal[scholar] ==> 2/2013
resultVal[scholar] ==> 3/2013
resultVal[contributor] ==> 2/2011
resultVal[teacher] ==> 3/2011
resultVal[student] ==> 6/2012
resultVal[tumbleweed] ==> 10/2012
You can try this...
var A = ['scholar', 'contributor',
'teacher', 'student', 'tumbleweed', 'scholar','scholar'];
var B = ['1/2010', '2/2011',
'3/2011', '6/2012', '10/2012', '2/2013','3/2013'];
var combined = {};
for(var i=0;i<A.length;i++) {
if(combined[A[i]] === undefined) {
combined[A[i]] = [];
}
combined[A[i]].push(B[i]);
}
Then each one of the arrays in combined can be accessed via
combined.scholar[0]
or
combined['scholar'][0]
Note the === when comparing against undefined

Categories