How can i nest data by same sized timespans like decades.
From this
[{key: "1432"}
{key: "1436"}
{key: "1557"}
{key: "1559"}
{key: "1834"}
{key: "1836"}
{key: "1839"}
{key: "undefined"}]
To this
[{key: "1430-1439", values: Array}
{key: "1550-1559", values: Array}
{key: "1830-1839", values: Array}
{key: "undefined", values: Array}]
Additionally it would be nice if the size of the timespan is calculated dynamically.
I solved the problem by looping over all of the elements and add the corresponding decade to it like here.
for(i = 0; i < dataToDraw.length; i++) {
if(dataToDraw[i].year != undefined) {
var decadeStart = dataToDraw[i].year - dataToDraw[i].year % 10;
dataToDraw[i].decade = decadeStart + "-" + (decadeStart + 9);
}
}
After that I used d3.nest to nest the data by the new decade field.
nestedDecadeData = d3.nest()
.key(function(d) { return d.decade; })
.sortKeys(d3.ascending)
.entries(dataToDraw);
Related
I'm trying to figure out how to sum accumulatives values over time of two arrays, seems simple, but here's what complicates it: there might be missing dates from one of them.
When one has a value for a date and the other doesn't, we sum together that value that exists from one array and the last-seen (previous) value for a date of the other array (The example I give demonstrates this better).
Example, given two arrays of objects where in data2 there's a value for a date that data1 doesn't have:
var data1 = [
{date: "30-08-2019", value: 1},
{date: "03-09-2019", value: 2},
{date: "04-09-2019", value: 3}
]
var data2 = [
{date: "30-08-2019", value: 1},
{date: "02-09-2019", value: 2},
{date: "03-09-2019", value: 3},
{date: "04-09-2019", value: 4}
]
I want the result of summing these two together (data1 + data2) to be:
var result = [
{date: "30-08-2019", value: 2}, //{date: "30-08-2019", value: 1} + {date: "30-08-2019", value: 1}
{date: "02-09-2019", value: 3}, //{date: "30-08-2019", value: 1} + {date: "02-09-2019", value: 2}
{date: "03-09-2019", value: 5}, //{date: "03-09-2019", value: 2} + {date: "03-09-2019", value: 3}
{date: "04-09-2019", value: 7} //{date: "04-09-2019", value: 3} + {date: "04-09-2019", value: 4}
]
Since both arrays are ordered, the approach I thought was looping the array with more data and sum it with the values of the array with less data, keeping track of what are the last date values that the smaller data gives, like this:
for(let i = 0; i < biggerData.length; i++){
//both have values for a date that exists the in bigger date array, so we sum them together
if(smallerData[i][biggerData[i].date]){
biggerData[i].value+=smallerData[i][biggerData[i].date];
lastValue = smallerData[i][biggerData[i].date];
//array with less data has a missing date, then sum the last saved value it gave
}else{
biggerData[i].value+=lastValue;
}
}
There's a problem with this approach, what if the smaller array has a date that the bigger one doesn't? That value one won't be added to the final result in this case.
When going more further than this I started to loop one array like I showed before and then I loop over the other one to get the missing dates, but this just seems too complex and inefficient. I'm pretty sure there's a solution for doing this in one loop (or even not using loops at all).
I'm asking if anybody can figure out a better solution for this, I'm making this in JavaScript.
I used a bunch of helper variables and converted the dates into an easily sortable format. Going through all existing dates in chronological order makes it quite easy to keep track of the accumulated value for each array. The sorting is the inefficient part, since the rest has linear complexity. You could optimize the sorting by taking advantage of the fact that both arrays are already sorted, but I was too lazy to do this here :)
// Turn '30-08-2019' into '2019-08-30'
const getSortableDate = (dateString) => dateString.split('-').reverse().join('-');
// Enable direct lookup of values
const mapDatesToValues = (data) => {
const dates = {};
data.forEach((item) => {
dates[getSortableDate(item.date)] = item.value;
});
return dates;
};
// Source data
const data1 = [
{date: "30-08-2019", value: 1},
{date: "03-09-2019", value: 2},
{date: "04-09-2019", value: 3}
];
const data2 = [
{date: "30-08-2019", value: 1},
{date: "02-09-2019", value: 2},
{date: "03-09-2019", value: 3},
{date: "04-09-2019", value: 4}
];
// values for direct lookup
const dates1 = mapDatesToValues(data1);
const dates2 = mapDatesToValues(data2);
// Chronological order for all existing dates
const allDatesOrdered = Object.keys({ ...dates1, ...dates2 }).sort();
// Helper variables:
let acc1 = 0; // Accumulated value while iterating through data1
let acc2 = 0; // Accumulated value while iterating through data2
let existsIn1;
let existsIn2;
let value1; // Current value while iterating through data1
let value2; // Current value while iterating through data2
allDatesOrdered.forEach((date) => {
existsIn1 = dates1.hasOwnProperty(date);
existsIn2 = dates2.hasOwnProperty(date);
value1 = dates1[date];
value2 = dates2[date];
// Remember accumulated values
if (existsIn1) {
acc1 = value1;
}
if (existsIn2) {
acc2 = value2;
}
if (existsIn1 && existsIn2) {
console.log('sum for', date, 'is', value1 + value2, '(found in both arrays)');
} else {
if (existsIn1) {
console.log('sum for', date, 'is', value1 + acc2, '(only found in data1)');
} else {
console.log('sum for', date, 'is', value2 + acc1, '(only found in data2)');
}
}
});
Figured out a way of going through this quite efficiently, but I converted the dates to millisecond timestamps due to beeing more suitable in the context I'm using this. Because of this change I'm not going to put my answer as the correct one.
#timotgl answer does it without converting date values so therefore I'm marking it the correct answer, although the solution also contains a change in the date format (that didn't helped me in my case but can help others).
I'm basically doing a zip function, going through both arrays and the merged result is being pushed in a result array of objects in one go.
data1.forEach((item) => {
item.date = new Date(item.date).getTime();
});
data2.forEach((item) => {
item.date = new Date(item.date).getTime();
});
let mergedPortfolio = [], //final array of objects
data1Idx = 0, //indexes for each array of objects
data2Idx = 0,
data1Last, //keeping track of last values
data2Last,
date1, //current date value
date2,
value1,//current value
value2;
while(data1Idx < data1.length || data2Idx < data2.length){
//both arrays exist
if(data1Idx < data1.length && data2Idx < data2.length){
date1 = data1[data1Idx].date;
date2 = data2[data2Idx].date;
value1 = data1[data1Idx].value;
value2 = data2[data2Idx].value;
if(date1 < date2){
mergedPortfolio.push({date: date1, value: value1+data2Last});
data1Last = value1;
++data1Idx;
}else if(data1[data1Idx].date === data2[data2Idx].date){
mergedPortfolio.push({date: date1, value: value1+value2})
data1Last = value1;
data2Last = value2;
++data1Idx;
++data2Idx;
}else if(data1[data1Idx].date > data2[data2Idx].date){
mergedPortfolio.push({date: date2, value: data1Last+value2});
data2Last = value2;
++data2Idx;
}
//Working through the remaining items in one data1 array
}else if(data1Idx < data1.length){
date1 = data1[data1Idx].date;
value1 = data1[data1Idx].value;
mergedPortfolio.push({date: date1, value: value1+data2Last});
data1Last = value1;
++data1Idx;
//Working through the remaining items in the data2 array
}else if(data2Idx > data2.length){
date2 = data2[data2Idx].date;
value2 = data2[data2Idx].value;
mergedPortfolio.push({date: date2, value: value2+data1Last});
data2Last = value1;
++data2Idx;
}
}
I have been trying to receive an object as a parameter and return it as an array but no luck. I don't see why it could be any problems in the code. I might be lost at this point or just completely out of the logic.
function att(testobj) {
var q = "";
for(var i = 0; i < testobj.length ; i++){
q += testobj[i] + " ";
}
return q;
}
var obj1= {
Big: 10,
low: 5
};
var attObj1= att(obj1);
console.log(attObj1);
var obj2= {
a: 10,
b: 20,
c: 30
};
var attObj2= att(obj2);
console.log(attObj2);
I did try as in the code do a for-loop where it check each array length and sort it by using q += testobj[i] but I'm not getting any results. Just a blank console log.
This article may help you object.keys()
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
var obj = { key0: 'value-0', key1: 'value-1', key2: 'value-2' };
console.log(Object.keys(obj)); // console: ['key0', 'key1', 'key2']
You could iterate over the keys and return the joind value.
Method used:
Object.keys for getting all keys from the object,
Array#join convert an array to a string with the given string as glue.
function att(testobj) {
return Object.keys(testobj).join(', ');
}
var obj1= {
Big: 10,
low: 5
};
var attObj1= att(obj1);
console.log(attObj1);
var obj2= {
a: 10,
b: 20,
c: 30
};
var attObj2= att(obj2);
console.log(attObj2);
Although an Array is technically an Object, one difference is that they can be iterated over.
So for example you can do:
var someArray = ['some', 'value', 'here'];
for (var i = 0; i <= someArray.length; i++) {
console.log(someArray[i]);
}
However, with an object you cannot as it doesn't contain the iterate functionality.
If you want to iterate through an Object and an Array interchangeably, you need to consider using a method that supports both.
For example:
function iterateOver(obj) {
for (var k in obj) {
console.log('Key', k, 'Value', obj[k]);
}
}
var someArray = ['some', 'value', 'here'];
var someObject = {
some: 'value',
another: 'great_value'
};
iterateOver(someArray);
iterateOver(someObject);
Assuming I have an array of data as follows:
var data = [{name: "craig", value: 10}, {name: "oliver", value: 15}]
I would like to use a function that allows parameters such as:
function separateTheWheatFromTheChaff(windowSize, pointsTaken, data, valueAccessor) {}
Where windowSize is the number of array positions to be evaluated, pointsTaken is the number of dataPoints to be returned.
So I know I'll need the sum from which to gain the mean. I will need to calculate the math.abs for each array position in relation to the mean and compare each result to find the datapoint array position furthest from the mean and then return that original datapoint value to a new array.
So far I have:
var data = [{name: "craig", value: 10}, {name: "oliver", value: -10}]
function separateTheWheatFromTheChaff(windowSize, pointsTaken, data, valueAccessor) {
var i;
sum = 0;
for(i = 0; i < windowSize; i++) {
sum += valueAccessor(data[i]);
}
mean = sum/windowSize
for (i = 0; i < windowSize; i++) {
Math.abs(valueAccessor(data[i]) - mean)
}
}
separateTheWheatFromTheChaff(5, 1, data, function(item) { return item.value });
So my question is, how would I need to amend the
separateTheWheatFromTheChaff function to calculate the array position
with the data point furthest from the mean, and return said datapoint
to a new array.
Thanks in advance, and I hope that makes sense.
Here's one way:
EDITED ANSWER:
//create an array to store all distances
var distances = [];
for (i = 0; i < windowSize; i++) {
//calculate distance from mean
var distance = Math.abs(valueAccessor(data[i]) - mean);
//store initial datapoint with its distance from mean in array
var datapoint_w_distance = {datapoint: data[i],
dist: distance}
distances.push(datapoint_w_distance)
}
//sort array so datapoint with largest distance from mean is first
distances.sort(function(a,b) {return b.dist-a.dist});
//use the pointsTaken parameter to get the correct number of original datapoints back
var wheat = [];
for (var j=0; j < pointsTaken; j++) {
wheat.push(distances[j].datapoint)
}
return wheat;
For example, if
var data = [{name: "a", value: 20},
{name: "b", value: 10},
{name: "c", value: 90},
{name: "d", value: 100},
{name: "e", value: 0}]
then separateTheWheatFromTheChaff(5, 2, data, function(item) { return item.value }) returns the array [{name: "d", value: 100}, {name: "c", value: 90}]
Working fiddle: https://jsfiddle.net/ubbrx3u3/5/
Here is the input:
[{animal: "cat"}, {animal:"dog}}
And the output would be :
[{animal: "cat", idx: 1}, {animal: "dog", idx: 2}]
Does anyone have ideas about how to do this in Lodash/Underscore.js?
In Underscore:
You could use either .map or .each to iterate across every element in the target array you were looking to add indexes to. Then you could use _.extend to add the "idx" prop.
Working Example:
var data = [{animal: "cat"}, {animal:"dog"}]
_.map(data, function(e, i) {
return _.extend(e, {idx: i + 1});
});
var data = [{animal: "cat"}, {animal:"dog"}, {animal:"er"}];
var data2 = _.select(data,function(val, key){
return {val, idx : key};
});
Loop across the elements, adding the index to each.
_.each(data, function(elt, i) { elt.idx = i + 1; })
var newAnimals = _.map(animals, function(animal, index){
animal.idx = index + 1;
return animal;
})
Json string having multiple data table string. I would like to split that JSON string to array format like this. I don't know how to convert this. Please help me to do that.
array[0] = 2:Dubstep;3:BoysIIMen;4:Sylenth1
array[1] = 11:Dubstep;12:BoysIIMen;13:Sylenth1
{"table":"[{value: 2,label: 'Dubstep'},{value: 3,label: 'BoysIIMen'},{value: 4,label:'Sylenth1'}]","table1":"[{value: 11,label: 'Dubstep'},{value: 12,label: 'BoysIIMen'},{value: 13,label:'Sylenth1'}]"}
I'm going to bet that there are more elegant solutions, but I've been able to parse your json and place it into a standard javascript array with two elements.
The down side is that it is not a "general solution" to your question. It doesn't read the names of the variables stored and does the process in two steps.
At least this is a start, and hopefully we'll get some more professional responses.
FIDDLE
JS
var mytables = {"table": [
{value: 2, label: 'Dubstep' },
{value: 3, label: 'BoysIIMen' },
{value: 4, label: 'Sylenth1'}
],
"table1":[
{value: 11, label: 'Dubstep'},
{value: 12, label: 'BoysIIMen'},
{value: 13, label: 'Sylenth1'}
]
};
var myarray1 = [];
var myarray2 = [];
var finalarray = [];
for(var n=0; n < mytables.table.length; n++)
{
myarray1.push( mytables.table[n].value + ':' + mytables.table[n].label + ';' );
myarray2.push( mytables.table1[n].value + ':' + mytables.table1[n].label + ';' );
}
finalarray.push( myarray1 );
finalarray.push( myarray2 );
$('.putmehere1').html( finalarray[0] );
$('.putmehere2').html( finalarray[1] );