-2
(14) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"}
1: {date: "2018-09-29", views: "69811", clicks: "361", likes: "5935"}
2: {date: "2017-12-30", views: "80107", clicks: "412", likes: "6526"}
3: {date: "2016-10-31", views: "88390", clicks: "445", likes: "6989"}

This is the array I have, I want to subtract object[0] with the object[1], and then with the value I got I want to print it and again, I want to subtract that value which I got previously with object[2] and get the value, print it and again subtract it with the object[3] and so on till the end and print the value in a table.

I want the end result of the array as given below

0: {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"}
1: {date: "2018-09-29", views: "35080", clicks: "153", likes: "2101"}
2: {date: "2017-12-30", views: "45027", clicks: "259", likes: "4425"}
3: {date: "2016-10-31", views: "43363", clicks: "186", likes: "2564"}
Abhi
  • 13
  • 5
  • What do you want to subtract? likes from day2 from likes from day1? – mplungjan Jan 11 '21 at 16:16
  • @mplungjan all the element including likes, clicks, etc. just not the date – Abhi Jan 11 '21 at 16:21
  • 1
    It would help us help you if you showed the result you're looking for, and stated more clearly what operation you want to do at each stage. For instance, what does it mean to subtract a date from date? Finding the number of days/hours/minutes/seconds/ms between them? And re the other fields, if you subtract the values in Object[1] from the values in Object[0] you'll get negative values. Is that really what you want? – T.J. Crowder Jan 11 '21 at 16:22
  • @T.J.Crowder sorry, my English is not good, but i showed the value what i want in the end – Abhi Jan 11 '21 at 16:31
  • @Abhi - I **strongly** recommend you don't start using `reduce` instead of simple for loops unless you're doing [functional programming](https://en.wikipedia.org/wiki/Functional_programming) with predefined, reusable reducer functions. It's way too easy to get it wrong. (**Note:** I don't care what answer you accept. :-) I just want to steer you clear of the mess so many people fall into with `reduce`. More [here](https://twitter.com/bterlson/status/1099010861065068544) and [here](https://twitter.com/jaffathecake/status/1213077702300852224).) – T.J. Crowder Jan 12 '21 at 09:28
  • @T.J.Crowder If *I* understand the reduce I write, anyone will understand it. I am not Nina Scholz ;)) – mplungjan Jan 12 '21 at 09:35
  • 1
    @mplungjan - I disagree, but LOL about Nina. :-) Yeah, she writes...amazingly terse code. Sometimes brilliant. But I wouldn't want to maintain it. :-D – T.J. Crowder Jan 12 '21 at 09:42
  • @Abhi Please re-read the comments and answers – mplungjan Jan 12 '21 at 09:53

2 Answers2

1

It looks like you're leaving the dates unchanged and subtracting the values in Object[0] from the ones in Object[1] (not vice-versa), repeatedly. This is most easily done with the entries in place, like this (see comments):

const array = [
    {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"},
    {date: "2018-09-29", views: "69811", clicks: "361", likes: "5935"},
    {date: "2017-12-30", views: "80107", clicks: "412", likes: "6526"},
    {date: "2016-10-31", views: "88390", clicks: "445", likes: "6989"},
];
// Start with the first entry
let lastEntry = array[0];
// Loop starting with the second entry
for (let index = 1; index < array.length; ++index) {
    // Get the entry for this loop iteration
    const entry = array[index];
    // Subtract the previous values
    entry.views -= lastEntry.views;
    entry.clicks -= lastEntry.clicks;
    entry.likes -= lastEntry.likes;
    // Remember this entry for the next loop iteration
    lastEntry = entry;
}

console.log(array);

But if you want a new set of objects in a new array, it's not a big change:

const original = [
    {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"},
    {date: "2018-09-29", views: "69811", clicks: "361", likes: "5935"},
    {date: "2017-12-30", views: "80107", clicks: "412", likes: "6526"},
    {date: "2016-10-31", views: "88390", clicks: "445", likes: "6989"},
];
const result = [];
if (original.length) {
    // Remember a copy of the first entry
    result.push({...original[0]});
    lastEntry = result[0];
    for (let index = 1; index < original.length; ++index) {
        // Get a copy of the original entry at this loop index
        const entry = {...original[index]};
        // Subtract the previous entry values
        entry.views -= lastEntry.views;
        entry.clicks -= lastEntry.clicks;
        entry.likes -= lastEntry.likes;
        // Save it
        result.push(entry);
        // Remember this for next time
        lastEntry = entry;
    }
}
console.log(result);

There I'm using ES2018+'s property spread syntax to do a shallow copy of the object. If you need to target older environments than that, you could use Object.assign (which was in the earlier ES2015 and is easily polyfilled) instead:

const entry = Object.assign({}, original[index]});

Or you could make the copy manually, combining it with the math:

const original = [
    {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"},
    {date: "2018-09-29", views: "69811", clicks: "361", likes: "5935"},
    {date: "2017-12-30", views: "80107", clicks: "412", likes: "6526"},
    {date: "2016-10-31", views: "88390", clicks: "445", likes: "6989"},
];
const result = [];
if (original.length) {
    // Remember a copy of the first entry
    result.push({...original[0]});
    lastEntry = result[0];
    for (let index = 1; index < original.length; ++index) {
        // Get a copy of the original entry at this loop index
        const source = original[index];
        const entry = {
            date: source.date,
            views: source.views - lastEntry.views,
            clicks: source.clicks - lastEntry.clicks,
            likes: source.likes - lastEntry.likes
        };
        // Save it
        result.push(entry);
        // Remember this for next time
        lastEntry = entry;
    }
}
console.log(result);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

Here is a version using reduce

NOTE: if you do not clone the original items, the code ALSO modifies the original which might not be what you want and is sort of ruining the reason for using reduce (forEach would then be better)

Here I clone via spread - thanks TJ for teaching me a lesson here

const original = [
    {date: "2019-11-28", views: "34731", clicks: "208", likes: "3834"},
    {date: "2018-09-29", views: "69811", clicks: "361", likes: "5935"},
    {date: "2017-12-30", views: "80107", clicks: "412", likes: "6526"},
    {date: "2016-10-31", views: "88390", clicks: "445", likes: "6989"},
];

const result = original.reduce((acc, _, i) => { // we do not use the current item
  const item = {...original[i]} // IMPORTANT property spread copy or you will modify the original

  if (i > 0) {
    const prev   = acc.length - 1;
    item.views  -= acc[prev].views;
    item.clicks -= acc[prev].clicks;
    item.likes  -= acc[prev].likes;
  }
  acc.push(item)
  return acc
}, [])
console.log(result);

console.log(original);
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • *"Here is a much simplified version of TJs middle version"* No, it isn't. A) It's not simpler, it just has fewer explanatory comments. (In fact, I'd argue `reduce` is always more complicated than a simple loop, outside FP with predefined, reusable reducers, but even leaving that aside.) B) It doesn't create a copy of the entries, which is what my middle version does. – T.J. Crowder Jan 12 '21 at 09:28
  • @T.J.Crowder Sorry, I do not follow. The reduce gives the exact same output as your for loop but without saving an item, assigning a new array first. Apologies if I did not get the problem at hand – mplungjan Jan 12 '21 at 09:31
  • 1
    The difference is that the above modifies the objects in the original array, but my "middle version" doesn't. Your result will be two different arrays with the same (modified) objects in them; `original[0] === result[0]` is true. My first version modifies the existing array's objects, and my middle version creates new objects in the new array. – T.J. Crowder Jan 12 '21 at 09:41
  • 1
    Ahh, that was the missing information I did not realise – mplungjan Jan 12 '21 at 09:42
  • @T.J.Crowder How come I modify the original even when I do `original.slice(0).reduce` ??? colour me surprised – mplungjan Jan 12 '21 at 09:46
  • I managed to clone the original using JSON, but I am a bit shaken here – mplungjan Jan 12 '21 at 09:50
  • 1
    `slice` copies the array, but not the objects in it. You're already creating a new array to put the objects in, so it doesn't make a difference. Basically you need `item = {...item};` or similar to copy each object before you modify it (my #2) or just create a new object listing the properties explicitly (#3). – T.J. Crowder Jan 12 '21 at 09:50
  • 1
    Just FWIW, I wouldn't use `JSON.parse(JSON.stringify(x))` to clone object trees. JSON can't handle circular references, drops functions, drops properties that exist but have the value `undefined`... If you want to deep clone, there are [good options here](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript), but you don't need deep cloning here, just shallow at the object level (unless there are constraints the OP hasn't given us). Regards and leaving you alone now! ;-) – T.J. Crowder Jan 12 '21 at 10:00
  • 1
    @T.J.Crowder So there was a good reason I bought your book ;))) My code seems to work now - I stole your spread and it works great – mplungjan Jan 12 '21 at 10:05