5

I want to use linq.js to group the following data by date.

data2 = [{
    "date": 1399298400.0,
    "adId": 1057946139383,
    "impressions": 1000000
  }, {
    "date": 1399298400.0,
    "adId": 3301784671323,
    "impressions": 535714
  }...... etc.
]; 

Here's my attempt:

var linq = Enumerable.From(data2);
data2 = linq.GroupBy(function (x) {
  return x.date;
}).Select(function (x) {
  return {
    date: x.Key(),
    impressions: x.Sum(function (y) {
      return y.impressions | 0;
    })
  };
}).ToArray();

However, It's not working correctly because the sum of all the impressions before and after the GroupBy are close but not identical.

What is the correct way to use group by in linq.js in this case?

Here's an example in fiddle with full dataset here which alerts the total impressions before and after using the GroupBy.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
user40721
  • 327
  • 3
  • 5
  • 13
  • seems to me that `| 0` is causing the problem. I copied this code from another stackoverflow post. What exactly does the `| 0` do? – user40721 May 16 '14 at 22:06
  • I should be `||` not `|` and it means that if the object to the left of the expression is `null` return whats to the right, which is `0`. – furier May 16 '14 at 23:03
  • Out of curiosity then - what does just the `\` do? – user40721 May 19 '14 at 17:15

4 Answers4

9

Solution

You can do this by passing a callback as the third parameter like this:

var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
  return { 
      person: key, 
      likes: g.sum("$.likes | 0")
 }
}).toArray()

Explanation

In groupBy, the third parameter allows you to modify the results before emitting:

GroupBy > ResultSelector

In JS, the bitwise or operator (a single pipe |) returns the first value if it exists, otherwise it returns the second one. Without it, trying to sum an undefined value with a real one, will return NaN

undefined + 1 // NaN

Without | 0, the result would look like this:

NaN

This example uses shorthand syntax, but if you prefer anytime you see a string with a dollar sign, you can replace it with the lambda syntax like this (they both do the same exact thing):

// Shorthand
.Select("$.impressions")
// Lambda
.Select(function (x) { return x.impressions })

Working demo with Stack Snippets:

var dataArray = [
  {
    person: "james",
    likes: 100
  }, 
  {
    person: "james",
    likes: 250
  }, 
  {
    person: "kyle",
    likes: 300
  }, 
  {
    person: "kyle"
    //,likes: 450
  }
];

var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
  return { person: key, likes: g.sum("$.likes | 0") }
}).toArray()

console.log(grouped);
<script src="https://unpkg.com/linq@3.2.0/linq.js"></script>

Further Reading:

KyleMit
  • 30,350
  • 66
  • 462
  • 664
4

I am the author of the open source project http://www.jinqJs.com. You can easily do that in jinqJs like this:

jinqJs().from(data2).groupBy('date').sum('impressions').select();

Let me know if I can be of anymore help.

NYTom
  • 524
  • 2
  • 14
0

You might try to group by date.toString(). Might be safer due to how JS evaluates dates equality

michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63
0

Alternatively, people coming into this question might have zero to a lot of buy in using linq.js.

If you're already pulling it in, go for it, but if this is the first couple real use cases for it, it's worth noting that you can accomplish the same thing in vanilla js:

For this data:

var dataArray = [
  { person: "james", likes: 100 }, 
  { person: "james", likes: 250 }, 
  { person: "kyle",  likes: 300 }, 
  { person: "kyle" }
];

You can build an object with properties for each key / person and keep summing the values of those props

var obj = dataArray.reduce((acc, cur) => {
    acc[cur.person] = (acc[cur.person] || 0) + (cur.likes || 0)
    return acc
}, {})

If you want that to be an array of objects, you can convert from an object to array like this

var array = Object.entries(obj).map(entry => {
    return { person: entry[0], likes: entry[1] }
})
KyleMit
  • 30,350
  • 66
  • 462
  • 664