1

I have this array of objects :

var data = [
  {
    "x": "2020-01-24T10:30:02.000Z",
    "y": 2
  },
  {
    "x": "2020-01-24T08:19:07.000Z",
    "y": 2
  },
  {
    "x": "2020-01-23T16:12:36.000Z",
    "y": 5
  },
  {
    "x": "2020-01-23T08:19:07.000Z",
    "y": 2
  }
]

I'm using ChartJs to display the data in a line chart, I don't know why it does not do this by it self, bu I want to combine the values that only have the same year+month+date without hours the output should be :

var data = [
  {
    "x": "2020-01-24T00:00:00.000Z",
    "y": 4
  },
  {
    "x": "2020-01-23T00:00:00.000Z",
    "y": 7
  }
]

The code I have so for :

const groupByDate = array => array.reduce((results, item) => {
  const current = results.find(i => new Date(i.x).getDate() === new Date(item.x).getDate());
  if (current) {
    for (let property in item) {
      if (property !== 'x') {
        current[property] = item[property];
      }
    }
  } else {
    results.push({...item});
  }
  return results;
}, []);

console.log(groupByDate(data));

But the function so far does not sum up the y values

6 Answers6

1

You could move find into the loop ans take just the first ten characters of the ISO 8601 date for comapring.

var data = [{ x: "2020-01-24T10:30:02.000Z", y: 2 }, { x: "2020-01-24T08:19:07.000Z", y: 2 }, { x: "2020-01-23T16:12:36.000Z", y: 5 }, { x: "2020-01-23T08:19:07.000Z", y: 2 }],
    result = [];
   
for (let { x, y } of data) {                             // iterate data, get x and y
    x = x.slice(0, 10);                                  // take yyyy-mm-dd only
    let temp = result.find(q => q.x.slice(0, 10) === x); // look for same data
    if (temp) temp.y += y;                               // if found add to y
    else result.push({ x: x + 'T00:00:00.000Z', y });    // if not create object and push
}

console.log(result);

A faster approach with an object.

var data = [{ x: "2020-01-24T10:30:02.000Z", y: 2 }, { x: "2020-01-24T08:19:07.000Z", y: 2 }, { x: "2020-01-23T16:12:36.000Z", y: 5 }, { x: "2020-01-23T08:19:07.000Z", y: 2 }],
    hash = Object.create(null),
    result;
   
for (let { x, y } of data) {
    x = x.slice(0, 10);
    if (hash[x]) hash[x] += y;
    else hash[x] = y;
}

result = Object
    .entries(hash)
    .map(([x, y]) => ({ x: x + 'T00:00:00.000Z', y }));

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Using slice in find for every iteration is not very efficient. `let temp = result.find(q => q.x.slice(0, 10) === x);` I did a quick test with 365*24*60 (log entries for every minute during a year), and it was 40 times slower than using a map (like I do in my answer). This took about 9 seconds while mine using a map took about 200 ms. – some Jan 24 '20 at 11:46
  • any hashing is (or should be) faster. but i think in near future, compilers uses hashing for `find` as well. the speed difference is then negligible. but anyway for four items ... – Nina Scholz Jan 24 '20 at 11:58
  • For four items it dosen't matter, but I think Rahmani Seif followed the guidelines of how to ask a question, and provided a minimal example, and that the real data has more entries. – some Jan 24 '20 at 12:07
  • Much better! Now it is only about 100% slower than mine. :) Thank you for reminding me of `Object.create(null)`. – some Jan 24 '20 at 12:52
0

You could use .reduce() to build an object, where you store each date as the key in the object, and the accumulated total object as the value. Then, to get these accumulated total objects, you can use Object.values() on the object built by .reduce():

const data = [ { "x": "2020-01-24T10:30:02.000Z", "y": 2 }, { "x": "2020-01-24T08:19:07.000Z", "y": 2 }, { "x": "2020-01-23T16:12:36.000Z", "y": 5 }, { "x": "2020-01-23T08:19:07.000Z", "y": 2 } ];

const res = Object.values(data.reduce((acc, obj) => {
  const key = obj.x.replace(/T.+/, ''); // get YYYY-MM-DD string
  acc[key] = acc[key] || {x: new Date(key).toISOString(),  y: 0}; // store date string as the key, ISO version of string part of the object.  
  acc[key].y += obj.y; // add to the total of the accumilated object
  return acc;
}, {}));

console.log(res);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
0

I'm using a Map to map the date to an object (faster than using find).

const data = [
  { "x": "2020-01-24T10:30:02.000Z", "y": 2 },
  { "x": "2020-01-24T08:19:07.000Z", "y": 2 },
  { "x": "2020-01-23T16:12:36.000Z", "y": 5 },
  { "x": "2020-01-23T08:19:07.000Z", "y": 2 }
]

function groupData(data) {
  const map = new Map();                                // create a map
  for (const {x,y} of data) {                           // iterate over data
    const date = x.slice(0,10);                         // Assume that data is ISO 8601
    const mapData = map.get(date);                      // Get object for date
    if (mapData) {                                      // Do it exist?
      mapData.y += y;                                   // Yes, add y.
    } else {
      map.set(date, { x:`${date}T00:00:00.000Z`, y });  // No, set a new object.
    }
  }
  return Array.from(map.values());                      // Return as an array
}

const result = groupData(data);
console.log(result);
some
  • 48,070
  • 14
  • 77
  • 93
0

I did it this way.

var returnData = [];

for (var i =0; i<data.length;i+=1){
  var found = 0;
    for(var j =0; j<returnData.length;j++){
      if(data[i].x.slice(0, 10)===returnData[j].x.slice(0, 10)){
        returnData[j].y += data[i].y
        found = 1;
      }
    }
  if(found===0){
    returnData.push(data[i]);
  }
}

console.log(returnData)
Mohit Kumar
  • 339
  • 1
  • 7
0
  • 1. Normalize Data Date by .map(...)
  • 2. Aggregate Entries by Date .reduce(...)
  • 3. Convert aggregation object to array of value objects Object.values(...)
var data = [
  { "x": "2020-01-24T10:30:02.000Z", "y": 2},
  { "x": "2020-01-24T08:19:07.000Z", "y": 2},
  { "x": "2020-01-23T16:12:36.000Z", "y": 5},
  { "x": "2020-01-23T08:19:07.000Z", "y": 2}
]

Object.values(data.map(entry => {
  return {
    x: entry.x.replace(/T.*$/, 'T00:00:00.000Z'),
    y: entry.y
  }
}).reduce((accumulator, entry) => {
  const bucket = accumulator[entry.x] = accumulator[entry.x] || {
    x: entry.x,
    y: 0
  }
  bucket.y += entry.y
  return accumulator;
}, {}))
qoomon
  • 4,549
  • 1
  • 21
  • 27
0
let newData = []
data.map((item,index) => {
 var total =    data.reduce((acc,val) => {
    console.log(item.x.substring(0,10))
    if(item.x.substring(0,10) == val.x.substring(0,10)){
        console.log('matching case')
        return acc + val.y
    }
    else {
        return acc + 0
    }
  },0)
  console.log('t',total)
  if(newData.filter(i => i.x.substring(0,10) === item.x.substring(0,10)).length === 0)
    newData.push({"x":item.x,"y":total})
})
Amir Saadallah
  • 668
  • 1
  • 8
  • 19