简体   繁体   中英

Combine two arrays of objects into one array for use in Google Charts

Before I start, yes I googled like crazy and read many answers on SO, but I just wasn't able to accomplish what needs to be done so I'm seeking help.

I have this problem that I just cannot solve. I have to display some data in a Google Annotation chart. The data I'm getting is in this format:

var arr1 = [{
    timestamp: "1505735024496", value1: 2
},
{
    timestamp: "1505815355920", value1: 5
},
.....
]

var arr2 = [{
    timestamp: "1505383687151", value2: 1
},
{
    timestamp: "1505485417374", value2: 3
},
.....
]

The end goal here is to combine these two arrays of objects into an array of arrays like this:

var final = [
[new Date(YYYY, DD, MM), value1, value2], 
[new Date(YYYY, DD, MM), value1, value2], 
...
]

as per instructions on the Google Charts documentation for that particular chart.

I only managed to convert timestamps to full dates with corresponding values for those dates, but that again gives me array of objects where I just converted the timestamp to a date and nothing much changed.

var indexesArr1 = arr1.map(function (obj) {
  return obj.timestamp;
});

var arr1New = arr1.map(function (obj) {
  var index = indexesArr1.indexOf(obj.timestamp);
  return { date: moment(parseInt(obj.timestamp)).format('YYYY, MM, DD'), value1: index > -1 ? arr1[index].value : obj.value };
});

The real problem here is that arr1 and arr2 don't have to be the same length (and probably won't be most of the time) and since this chart will display data over time, it's important to populate every date with both value1 and value2, even thought one of those values might not be explicitly returned for that particular day. And one more thing would be to remove all instances of duplicate dates, but leave the last one, since that one represents the last logged value for that particular date.

I've used Moment.js to easily convert timestamps to desired format('YYYY, DD, MM'), but I just cannot do all the data manipulation properly.

Use of Underscore.js is allowed since I know it has some great utility functions for these types of problems, but I just wasn't able to solve it (don't have that much experience with it to be honest) and I know there are people here with more knowledge than me so I'm seeking help.

Thank you in advance for taking your time to answer this.

PS I would link to Moment.js but I don't have enough reputation to post more than 2 links currently.

recommend creating two google data tables from the arrays...

var arr1 = [{
  timestamp: "1505735024496", value1: 1
}, {
  timestamp: "1505735034496", value1: 2
}, {
  timestamp: "1505815355920", value1: 5
}];

var arr2 = [{
  timestamp: "1505383687151", value2: 1
}, {
  timestamp: "1505485417374", value2: 3
}];

var data1 = google.visualization.arrayToDataTable(arr1.map(function (row) {
  return [new Date(parseInt(row.timestamp)), row.value1];
}), true);

var data2 = google.visualization.arrayToDataTable(arr2.map(function (row) {
  return [new Date(parseInt(row.timestamp)), row.value2];
}), true);

then you can join the data tables on the timestamp

var dataJoin = google.visualization.data.join(
  data1,
  data2,
  'full',
  [[0, 0]],
  [1],
  [1]
);

the join will use null values for each column where the dates do not match,
which can be replaced with zeroes using a data view

AnnotationChart doesn't support option interpolateNulls ,
other charts, you could leave the nulls

to address missing and multiple dates,
use data table method getColumnRange to find the min and max dates in the table

then starting with the min date, until the max date,
use data table method getFilteredRows to check for each date in between
use only the date portion, drop the time values

if the date doesn't exist, add a new row
if date exists multiple times, delete all but last

see following working snippet...

 google.charts.load('current', { packages: ['annotationchart', 'table'] }).then(function () { // date formatter var formatDate = new google.visualization.DateFormat({ pattern: 'yyyy-MM-dd' }); // original arrays var arr1 = [{ timestamp: "1505735024496", value1: 1 }, { timestamp: "1505735034496", value1: 2 }, { timestamp: "1505815355920", value1: 5 }]; var arr2 = [{ timestamp: "1505383687151", value2: 1 }, { timestamp: "1505485417374", value2: 3 }]; // create google data table for each array var data1 = google.visualization.arrayToDataTable(arr1.map(function (row) { return [new Date(parseInt(row.timestamp)), row.value1]; }), true); var data2 = google.visualization.arrayToDataTable(arr2.map(function (row) { return [new Date(parseInt(row.timestamp)), row.value2]; }), true); // join data tables var dataJoin = google.visualization.data.join( data1, data2, 'full', [[0, 0]], [1], [1] ); // sort join data table to ensure latest date is last dataJoin.sort([{column: 0}]); // one day time var oneDay = (1000 * 60 * 60 * 24); // get date range var dateRange = dataJoin.getColumnRange(0); // drop time values from min and max dates var begDate = new Date(dateRange.min.getFullYear(), dateRange.min.getMonth(), dateRange.min.getDate()); var endDate = new Date(dateRange.max.getFullYear(), dateRange.max.getMonth(), dateRange.max.getDate()); // check each date for (var iDate = begDate.getTime(); iDate <= endDate.getTime(); iDate = iDate + oneDay) { // find date var findDate = new Date(iDate); var dateRows = dataJoin.getFilteredRows([{ column: 0, test: function (rowValue) { var rowDate = formatDate.formatValue(rowValue); var testDate = formatDate.formatValue(findDate); return (rowDate === testDate); } }]); // getFilteredRows will return array of row indexes switch (dateRows.length) { // add row case 0: dataJoin.addRow([findDate, null, null]); break; case 1: break; // remove all duplicates except the last row index found default: for (var iRow = dateRows.length - 2; iRow >= 0; iRow = iRow - 1) { dataJoin.removeRow(dateRows[iRow]); } } } dataJoin.sort([{column: 0}]); // replace null values with zeroes var dataView = new google.visualization.DataView(dataJoin); dataView.setColumns([0, { calc: function (dt, row) { return dt.getValue(row, 1) || 0; }, label: 'y0', type: 'number' }, { calc: function (dt, row) { return dt.getValue(row, 2) || 0; }, label: 'y1', type: 'number' }]); // draw annotation chart var containerChart = document.getElementById('chart_div'); var chart = new google.visualization.AnnotationChart(containerChart); chart.draw(dataView); // draw table for testing purposes, "see" the data var containerTable = document.getElementById('table_div'); var table = new google.visualization.Table(containerTable); table.draw(dataView); }); 
 <script src="https://www.gstatic.com/charts/loader.js"></script> <div id="chart_div"></div> <div id="table_div"></div> 

Might not be a direct answer to your question, but have you ever thought of using the timestamp as your object key? It would be way more performant to use it like that than to loop over it when assigning new values to existing objects.

eg

let obj1 = {
    '34123812371293': 2,
  '12314981391230': 3,
}
let obj2 = {
    '13812381238123' : 1,
    '19312930812930' : 3,
  '12314981391230': 100,
}

let combinedObj = Object.assign({}, obj1);
Object.keys(obj2).map(o2 => {
    if(combinedObj[o2] != undefined){
    combinedObj[o2] += obj2[o2];
  }else{
    combinedObj[o2] = obj2[o2];
  }
});
console.log(combinedObj);

That way when you pull in new data you can directly compare the new datas object key to your existing object and increase the value or add a new key pair.

https://jsfiddle.net/z23xeaxm/

Maybe you could merge those two arrays into a new one. Something like this :

var arr1 = [
{timestamp: "1505735024496", value1: 2},
{timestamp: "1505815355920", value1: 5}
]

var arr2 = [
{timestamp: "1505383687151", value2: 1},
{timestamp: "1505485417374", value2: 3}
]

var merged = [];
merged.push(arr1);
merged.push(arr2)

Hope this helps..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM