I have a CSV file with data as such:
,tests,Mary,Joe,Frank
0,Test1,92,15,72
1,Test2,74,25,70
2,Test3,30,65,50
3,Test4,82,80,48
this data has been taken from a space delimited text file, converted into a dataframe in pandas, and then converted into a csv file for use in a d3.js driven scatterplot.
I want to bind data to points so each dot on that scatterplot would hold info like
studentname: Joe
Test1: 15
Test2: 25
Test3: 65
Test4: 80
for each student and each test. I have a function like
d3.csv('path/to/file.csv', function(error, data) {
if error throw error;
data.forEach(function(d) {
d.studentname = d.name;
d.test = d.tests;
d.score = d.studentscore;
})});
which I know doesn't access the data the way I want it to. How can I get the function to pick out the data I want correctly?
First off, your CSV will not work as you haven't got a name for the first element in the header.
If you did, and you read the first element of the dataset you will get something like :
randomname(this i made up) : 0
tests : Test1
mary : 92
joe : 15
frank : 72
So to get it in the format you want you will have to loop through this and make a new data set, something like so (bare in mind I converted this to JSON to work with in JSFiddle easily). There probably is easier ways too :
Create individual student objects :
var newData = [];
for (var i in data[0]) { //create individual student objects
if (i != 'randomname' && i != 'tests') {
newData.push({
studentName: i
})
}
}
Create array to get index of individual student. I did this to make it easier to retrieve the individual student objects to give them test data :
var elementPos = newData.map(function(x) {
return x.studentName; //create array to get index of each student
});
Now loop through the data, find each student and give them a test score :
for (var i = 0; i < data.length; i++) { //loop through data
for (var j in data[i]) { //loop through each element in data[i]
if (j != 'tests' && j != 'randomname') { //get all students test scores
var thisStudent = newData[elementPos.indexOf(j)]; //get student object array based on index
thisStudent["test" + i] = data[i][j]; //set testscore, incremented
}
}
}
Working fiddle : https://jsfiddle.net/thatOneGuy/h7b2p2eh/1/
Working code :
var data = [{
"randomname": 0,
"tests": "Test1",
"Mary": 92,
"Joe": 15,
"Frank": 72
}, {
"randomname": 1,
"tests": "Test2",
"Mary": 74,
"Joe": 25,
"Frank": 70
}, {
"randomname": 2,
"tests": "Test3",
"Mary": 30,
"Joe": 65,
"Frank": 50
}, {
"randomname": 3,
"tests": "Test4",
"Mary": 82,
"Joe": 80,
"Frank": 48
}];
var newData = [];
for (var i in data[0]) { //create individual student objects
if (i != 'randomname' && i != 'tests') {
newData.push({
studentName: i
})
}
}
var elementPos = newData.map(function(x) {
return x.studentName; //create array to get index of each student
});
for (var i = 0; i < data.length; i++) { //loop through data
for (var j in data[i]) { //loop through each element in data[i]
if (j != 'tests' && j != 'randomname') { //get all students test scores
var thisStudent = newData[elementPos.indexOf(j)]; //get student object array based on index
thisStudent["test" + i] = data[i][j]; //set testscore
}
}
}
console.log(newData)
Related
I get issues when I want to loop through a JSON array of objects.
Issues such as:
It only counts two (I assume because of they Object.keys) and I have two keys.
Loops with only one value
My code:
var codes = require('./nl.json');
for (var i = 0, l = Object.keys(codes).length; i <= l; i++) {
console.log(l) ;
var areaCodeTest = codes.netherlands[i].areaCode;
var areaNameTest = codes.netherlands[i].areaName;
it("Search for postal code ", function(){
var postCode = element(by.id("imysearchstring"));
postCode.click();
browser.sleep(1000);
console.log(areaCodeTest);
postCode.clear().sendKeys(areaCodeTest);
browser.sleep(1000);
console.log("Typed " + areaCodeTest);
});
}
My Json (Short example):
{
"netherlands": [
{
"areaCode": 9401,
"areaName": "Assen"
},
{
"areaCode": 9402,
"areaName": "Assen"
},
{
"areaCode": 9403,
"areaName": "Assen"
}
]
}
I have looked at answers such as :
Size of Object and
Length of Json
I have tried:
(var i = 0, l = Object.keys(codes).length; i <= l; i++)
(var i = 0, l = Object.keys(codes.netherlands[0]).length; i <= l; i++)
for (var i = 0, l = codes.netherlands.length; i <= l; i++) // uses last areaCode in json file and only loop with that number. It does not start from top.
Image:
some of my outputs
Expected:
What I want is to count amount of ofjects in JSON (Not the key/values)
Loop through all data and assign them to var areaCodeTest = codes.netherlands[i].areaCode; and var areaNameTest = codes.netherlands[i].areaName;
I got it to work by using the following:
var codes = require('./nl.json');
codes.forEach((item) => {
var areaCodeTest = item.areaCode;
var areaNameTest = item.areaName;
it("and search for postal code ", function(){
var postCode = element(by.id("imysearchstring"));
postCode.click();
console.log(areaCodeTest);
postCode.clear().sendKeys(areaCodeTest);
browser.sleep(1000);
console.log("Typed " + areaCodeTest);
});
}
I am not a 100% what the => means near the foreach but I am currently researching why my code works. If you know please post a comment so that other developers also learn.
This let me think of the meme "not sure why code does not work / Not sure why code works"
You need to access the actual key in your loop in order to access codes[key]
Simplified version of your for() loop with stored variable for the object keys or using for in loop
const keys = Object.keys(codes)
for (let i = 0; i < keys.length; i++) {
// current object key and value of that property in main object
const key = keys[i], arr = codes[key];
console.log(`key = ${key}, length= ${arr.length}`)
// do another loop here for `arr` if needed
}
// OR using `for in`
for (let key in codes) {
console.log(`key = ${key}, length= ${codes[key].length}`)
}
<script>
const codes = {
"netherlands": [{
"areaCode": 9401,
"areaName": "Assen"
},
{
"areaCode": 9402,
"areaName": "Assen"
},
{
"areaCode": 9403,
"areaName": "Assen"
}
]
}
</script>
Try this I give you a sample
const object1 = {
a: 'somestring',
b: 42,
c: false
};
var length = (Object.keys(object1).length);
Please Refer this Link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
I've got some CSV data that looks like this, showing pass rates by organisation by year:
org,org_cat,2004_passed,2004_total,2005_passed,2005_total,2006_passed,2006_total
GSK,industry,35,100,45,100,55,100
I'm working in D3, and I'd like to end up with a dictionary of organisations like this:
data = {
'GSK': {
'org_cat': 'industry',
'data': [
{ 'year': 2004, 'passed': 35, 'total': 100 },
{ 'year': 2005, 'passed': 45, 'total': 100 },
{ 'year': 2006, 'passed': 55, 'total': 100 }
]
]
}
Most of this is straightforward, but I've got very messy code for the year columns:
var data = {};
allData.forEach(function(d) {
data[d.org] = {
'category': d.org_cat,
'data': []
};
for (var k in d) {
var temp = {};
for (var k in d) {
if (patt.test(k)) {
var res = k.split("_");
if (res[0] in temp) {
temp[res[0]][res[1]] = +d[k];
} else {
temp[res[0]] = {};
temp[res[0]][res[1]] = +d[k];
}
}
}
var tempArr = [];
for (var y in temp) {
var tempDict = {};
tempDict.year = y;
tempDict.passed = temp[y].passed;
tempDict.total = temp[y].total;
tempArr.push(tempDict);
}
// TODO: sort by year in case the keys got shuffled
data[d.org].data = tempArr;
}
});
Is there a way to simplify this horrid code?
It's safe to assume that each row is a unique organisation.
I don't see why you need D3 to do this. You're code doesn't use it either. Here's how I would do it, I'm sure there's another simpler way but it may help you either way :
Jsfiddle : https://jsfiddle.net/thatOneGuy/dnvheznk/1/
I converted the data to JSON to use with JSFiddle, but you already know how to loop through CSV so just overwrite line 14 :
for (var i = 0; i < data.length; i++) { //loop through data array (this is so you can use this on a bigger sized array)
To your loop :
allData.forEach(function(d, i) { //but add the 'i' to index
Here is full commented code with converted JSON data :
var data = [{ //data set converted to JSON for easier use
"org": "GSK",
"org_cat": "industry",
"2004_passed": 35,
"2004_total": 100,
"2005_passed": 45,
"2005_total": 100,
"2006_passed": 55,
"2006_total": 100
}];
var newData = {}; //new data container
for (var i = 0; i < data.length; i++) { //loop through data array (this is so you can use this on a bigger sized array)
var thisObj = {}; //create empty object
thisObj.org_cat = data[i].org_cat; //set org_cat
thisObj.data = []; //set data to empty array to populate later
for (var key in data[i]) { //loop through data[i]
if (key != 'org' && key != 'org_cat') { //check this key is not org or org_cat
var thisData = {}; //create empty data object
var thisYear = key.toString().substring(0, 4); //get year by using substring
thisData.year = thisYear; //set year
thisData.passed = data[i][thisYear + '_passed']; //set passed at this year
thisData.total = data[i][thisYear + '_total']; //set total at this year
thisObj.data.push(thisData); //push this data to data array
}
}
var uniqueDates = []; //set empty array to use to remove duplicate items in data array
for (var j = 0; j < thisObj.data.length; j++) { //loop through data array created earlier
if (uniqueDates.indexOf(thisObj.data[j].year) < 0) { //if this year doesn't exist in unique array above, push it in
uniqueDates.push(thisObj.data[j].year); //push it in
} else {
thisObj.data.splice(j--, 1); //remove duplicate data
}
}
newData[data[i].org] = thisObj; //set data # current org to be object created above
}
console.log('newData', newData) //log new data
So this is a little trippy, but here goes.
Let's say I have two arrays-- a 1D array that contains set of of categories, and a 2D array that contains an arbitrary number of arrays whose contents follow the category templates established by the first array. For instance:
var categoryArray = ["Name", "Title", "Hire Date"];
var infoArray = [["John","Project Manager","January 5"], ["Alex","Accountant","December 15"], ["Joanne","Graphic Designer","August 26"]];
What I'd like to do is consolidate this information into a single object. So up top I drop:
var myDict = {};
My (obviously wrong) attempt was to have nested for-loops which go through each array and attempts to fill the myDict object with the contents of the arrays. It looks like this:
// Start by iterating by the length of the info array, so we assign a new sub-object to the myDict Object for each entry.
for (i = 0; i < infoArray.length; i++) {
// Each entry will be a new "row," like in a spreadsheet.
var row = String("row"+i);
// I'm guessing that the declaration below doesn't actually assign the "Row1", "Row2", etc as nested objects like I had intended, but just re-writes a child called "row"
myDict.row = {};
// Next we iterate through the number of categories we'll need-- and we'll pull from the length of our categoryArray so we can change the number of categories later.
for (x = 0; x < categoryArray.length; x++) {
// In theory, the first iteration of this will create a child of "row1" called "name," which will hold a value of "John" (so, the value stored infoArray[1][1])
myDict.row.categoryArray[x] = infoArray[i][x];
}
}
There's clearly a LOT wrong in there, but I'm totally scratching my head about how to approach this. I guess the key problem is trying to assign the actual strings held by variables/arrays as names for objects/children, but a thorough googling has produced no answers.
Save me oh mighty internet!
var result = infoArray.map(function(currentArray) {
return categoryArray.reduce(function(previous, currentKey, index) {
previous[currentKey] = currentArray[index];
return previous;
}, {});
});
Output
[ { Name: 'John',
Title: 'Project Manager',
'Hire Date': 'January 5' },
{ Name: 'Alex',
Title: 'Accountant',
'Hire Date': 'December 15' },
{ Name: 'Joanne',
Title: 'Graphic Designer',
'Hire Date': 'August 26' } ]
If your environment doesn't support Array.prototype.map function, you can use this
var result = [];
for (var i = 0; i < infoArray.length; i += 1) {
var tempObj = {};
for (var j = 0; j < categoryArray.length; j += 1) {
tempObj[categoryArray[j]] = infoArray[i][j];
}
result.push(tempObj);
}
Using row0, row1, etc. doesn't sound like a good idea. An array would make more sense. Anyway:
var categoryArray = ["Name", "Title", "Hire Date"];
var infoArray = [["John","Project Manager","January 5"],["Alex","Accountant","December 15"],["Joanne","Graphic Designer","August 26"]];
var myDict = {};
for (i = 0; i < infoArray.length; i++) {
// Each entry will be a new "row," like in a spreadsheet.
var row = {};
myDict["row"+i] = row;
// Next we iterate through the number of categories we'll need-- and we'll pull from the length of our categoryArray so we can change the number of categories later.
for (x = 0; x < categoryArray.length; x++) {
// In theory, the first iteration of this will create a child of "row1" called "name," which will hold a value of "John" (so, the value stored infoArray[1][1])
row[categoryArray[x]] = infoArray[i][x];
}
}
Output:
{
"row0": {
"Name": "John",
"Title": "Project Manager",
"Hire Date": "January 5"
},
"row1": {
"Name": "Alex",
"Title": "Accountant",
"Hire Date": "December 15"
},
"row2": {
"Name": "Joanne",
"Title": "Graphic Designer",
"Hire Date": "August 26"
}
}
// I'm guessing that the declaration below doesn't actually assign the "Row1", "Row2", etc as nested objects like I had intended, but just re-writes a child called "row"
myDict.row = {};
Exactly. For that, you will have to use
myDict[row] = {};
(and the same again below: myDict[row][…] = …)
// In theory, the first iteration of this will create a child of "row1" called "name," which will hold a value of "John" (so, the value stored infoArray[1][1])
myDict.row.categoryArray[x] = infoArray[i][x];
Again .categoryArray[x] is here used as a literal property name (the property x of the property "categoryArray", which does not exist) - you will have to wrap it in brackets.
You should end up with:
var myDict = {};
for (var i = 0; i < infoArray.length; i++) { // make iteration variables local
// with "var"
var row = "row"+i; // explicit String() call not necessary
myDict[row] = {};
for (var x = 0; x < categoryArray.length; x++) { // again, local "x"
myDict[row][categoryArray[x]] = infoArray[i][x];
}
}
I have data that comes from my server to datatables.
I'm successfully populating my table but in footer callback I want to do some statistics.
Lets say I have data like so:
var data = [{
date: '2013-05-12',
holiday: "One type of holiday",
dayType: "Weekend"
}, {
date: '2013-05-13',
holiday: "Another type",
dayType: "Weekend"
}, {
date: '2013-05-14',
holiday: "Another type",
dayType: "Work"
}, {
date: '2013-05-15',
holiday: "",
dayType: "Work"
}];
I would like to count number of days with different holidays.
Here is result I would like to get:
var summary= [
{
"One type of holiday": {
"work": 0,
"weekend": 1
}
},
{
"Another type": {
"work": 1,
"weekend": 1
}
}];
I've created a very simple code to simply aggregate holidays:
for (var i = 0; i < data.length; i++) {
//console.log(data[i].holiday);
/*other stuff here*/
if (data[i].holiday.length > 0)
summary[data[i].holiday] = summary[data[i].holiday] + 1 || 1;
}
but this gives me invalid results, because in my data array holiday contains spaces.
I need a way to fix this and to split holidays based on dayType.
MY SOLUTION:
My version of answer:
var summary = {}, d, tmp, type;
for (var i = 0; i < data.length; i++) {
var d = data[i];
if (d.holiday.length > 0) {
type = d.dayType == 'Weekend' || d.dayType == 'Free' ? 'Weekend' : 'Work';
tmp = summary[d.holiday];
if (!tmp) {
tmp = {
Weekend: 0,
Work: 0
};
summary[d.holiday] = tmp;
}
summary[d.holiday][type] += 1;
}
}
Because this is modified version of #Arun answer I'm not posting this as standalone answer.
I find my version easier to understand, hope someone find's it useful.
Try
var summary = [], summaryMap = {}, d, map, m;
for (var i = 0; i < data.length; i++) {
var d = data[i];
map = summaryMap[d.holiday];
if(!map){
map = {
Work: 0,
Weekend: 0
};
m = {};
m[d.holiday] = map;
summary.push(m);
summaryMap[d.holiday] = map;
}
map[d.dayType] += 1;
}
console.log(summary);
console.log(JSON.stringify(summary));
Demo: Fiddle
go for
console.log(Object.keys(summary).length);
instead of
console.log(summary.length);
Because you can get the number of elements in a js object by using the length attribute.
note: using Object.keys may lead you to browser compatibility issues. As its supported form IE 9 and Firefox 4. See more info in this MDN article.
you can find more info and solutions for this problem in this answer.
see the updated fiddle.
Here's my attempt:
var summary = [];
var holidayTypes = [];
var dayTypes = [];
//first work out the different types of holidays
for (var i = 0; i < data.length; i++) {
if(holidayTypes.indexOf(data[i].holiday) == -1){
//this is a new type of holiday
holidayTypes.push(data[i].holiday);
}
if(dayTypes.indexOf(data[i].dayType) == -1){
//new type of day.
dayTypes.push(data[i].dayType);
}
}
console.log('types of holiday: ' + JSON.stringify(holidayTypes));
console.log('types of day: ' + JSON.stringify(dayTypes));
for(index in holidayTypes){
var typeobj = {};
//create an object for each type of holiday
typeobj[holidayTypes[index]] = {};
for(index2 in dayTypes){
//initialize a count for each type of day
typeobj[holidayTypes[index]][dayTypes[index2]] = 0;
//iterate through the data and count the occurrences where the day AND holiday match.
//if they do, iterate the value.
for (var j = 0; j < data.length; j++){
if((data[j].holiday == holidayTypes[index])
&& (data[j].dayType == dayTypes[index2])){
typeobj[holidayTypes[index]][dayTypes[index2]]++;
}
}
}
summary.push(typeobj);
}
console.log(JSON.stringify(summary));
Fiddle here
Output:
[{"One type of holiday":{"Weekend":1,"Work":0}},{"Another type":{"Weekend":1,"Work":1}},{"":{"Weekend":0,"Work":1}}]
It works but is unlikely to be as efficient as the guys above!
I'm probably going about this in entirely the wrong way, but pseudocode-wise, this is what I'd like to do:
ranges = new Array([0 - 400] = 'dog',
[401 - 1000] = 'cat',
[1001 - 1233] = 'rat');
alert(ranges[243]);
and magically get 'dog' pop up on my screen. Now, I'm well aware you can't do something that simple, but I have absolutely no idea how to do something like this (without looping through an array, performing a > and < on a value etc..)
Any ideas? :)
Well you could populate a simple array with all the duplicates:
var i,
ranges = [];
for (i = 0; i <= 400; i++) ranges[i] = "dog";
for (i = 401; i <= 1000; i++) ranges[i] = "cat";
for (i = 1001; i <= 1233; i++) ranges[i] = "rat";
alert(ranges[243]); // "dog"
A more sensible option might be to use some kind of data structure that actually holds the low and high values of each range:
var ranges = [
{ "low" : 0, "high" : 400, "animal" : "dog" },
{ "low" : 401, "high" : 1000, "animal" : "cat" },
{ "low" : 1001, "high" : 1233, "animal" : "rat" }
];
function getAnimal(index) {
for (var i=0; i < ranges.length; i++)
if (ranges[i].low <= index && ranges[i].high >= index)
return ranges[i].animal;
return "Out of range"; // or could leave it to return undefined
}
alert(getAnimal(243)); // "dog"
alert(getAnimal(100000)); // "Out of range"
Or you could combine the two approaches: pass the ranges data structure with the low and high values to a function that uses the information to populate another array as per the first approach.
This one is very simple and straightforward - though it assumes that you don't want "holes" in your ranges. No error handling in this example, but essentially you just need to pass upper bounds, lower bounds are implicit.
function test() {
var array = MakeRangeArray([500,1000,2000], ['dog', 'cat', 'rat']);
alert (array[243]);
alert (array[500]);
alert (array[501]);
}
function MakeRangeArray(upperBounds, values) {
var rangeArray = new Array(upperBounds[upperBounds.length-1]);
var idx = 0;
for (var i=0; i < rangeArray.length; i++) {
if (i > upperBounds[idx]) {
idx++;
}
rangeArray[i] = values[idx];
}
return rangeArray;
}
You could fully populate an array sp you can dp a direct index into the array at run-time. Once the original data structure is created, that will be fast at run-time, but not very storage efficient. I generally wouldn't recommend this, but to build the array:
var ranges = [
{min: 0, max: 400, value: 'dog'},
{min: 401, max: 1000, value: 'cat'},
{min: 1001, max: 1233, value: 'rat'}
];
var lookupArray = [];
for (var i = 0; i < ranges.length; i++) {
// make sure array is big enough
lookupArray.length = Math.max(lookupArray.length, ranges[i].max);
for (var j = ranges[i].min, j <= ranges[i].max; j++) {
lookupArray[j] = ranges[i].value;
}
}
// function to find the value for a given index
function findValue(index) {
return(lookupArray[index]);
}
Or, in a more compact structure, you can use a data structure and function like this:
var ranges = [
{min: 0, max: 400, value: 'dog'},
{min: 401, max: 1000, value: 'cat'},
{min: 1001, max: 1233, value: 'rat'}
];
function findValue(index) {
var range;
for (var i = 0; i < ranges.length; i++) {
range = ranges[i];
if (index >= range.min && index <= range.max) {
return(range.value);
}
}
}
alert(findValue(402)); // 'cat'
You could also use a straight array with implicit positions in the array (code and data are a little more compact, but both are a bit less readable):
var ranges = [
0, 400, 'dog',
401, 1000, 'cat',
1001, 1233, 'rat'
];
function findValue(index) {
for (var i = 0; i < ranges.length; i+=3) {
if (index >= ranges[i] && index <= ranges[i+1]) {
return(ranges[i+2];
}
}
}