سؤال

I have a simple set of JSON that needs to be reformatted since all of the key value pairs are not always output.

{
"result": [
    {
        "category": "Negative Notification",
        "event": "open",
        "result": 2
    },
    {
        "category": "Referral",
        "event": "bounce",
        "result": 1
    },
    {
        "category": "Negative Notification",
        "event": "delivered",
        "result": 34
    },
    {
        "category": "Negative Notification",
        "event": "processed",
        "result": 34
    },
    {
        "category": "Positive Notification",
        "event": "open",
        "result": 42
    },
    {
        "category": "Referral",
        "event": "delivered",
        "result": 17
    },
    {
        "category": "Positive Notification",
        "event": "processed",
        "result": 504
    },
    {
        "category": "Referral",
        "event": "processed",
        "result": 18
    },
    {
        "category": "Positive Notification",
        "event": "delivered",
        "result": 504
    },
    {
        "category": "Negative Notification",
        "event": "bounce",
        "result": 16
    },
    {
        "category": "Positive Notification",
        "event": "bounce",
        "result": 176
    },
    {
        "category": "Referral",
        "event": "open",
        "result": 10
    }
]
}

The problem with the way this is output is depending on weather the data is available or not, accessing the objects by number could create unexpected functionality. The second problem is that is has to be manipulated by javascript, and cannot be manipulated on the server side.

I would like the JSON to be reformatted so that each category is an object (there are currently three, but could be as many as five) has summarized data inside the object. For example:

{
"result": {
    "Negative Notification" : [ 
        {
        "processed":34,
        "delivered":34,
        "bounces":16,
        "opens":2
        }
    ],
    "Positive Notification" : [
        {
        "processed":504,
        "delivered":504,
        "bounces":176,
        "opens":42
        }
    ],
    "Referral" : [
        {
        "processed":18,
        "delivered":17,
        "bounces":1,
        "opens":10
        }
    ]

}
}

How would I pull this off? Simply looping through and naming the objects is leading me nowhere.

هل كانت مفيدة؟

المحلول

As an addition to T.J Crowder's suggestion: the same basic principle, only without relying on an ES5 function, nor requiring a shim. Note: This solution does differ from your "desired output" in one way: rather than having each category reference an array that has only 1 element (an object literal), I just assign it an object literal directly. Your desired format would require you to access the bounced referrals like so: obj.result.Referral[0].bounces, whereas I think it makes more sense if it were obj.result.Referral.bounces, without the array in between.

//suppose a is the raw JSON data
var b = {result:{}};//this will become be the object you want
for (var i=0;i<a.result.length;i++)
{
    b.result[a.result[i].category] = (function(obj)
    {
        var p, res = {};
        for (p in obj)
        {
            if (p !== 'category' && obj.hasOwnProperty(p))
            {
                res[p] = obj[p];
            }
        }
        return res;
    }(a.result[i]));
}

This loops through the array, referenced by a.result, each time using the value of a.result[i].category as a property name for an object that holds the other data.
The result is:

console.log(JSON.stringify(b));
{"result":
    {"Negative Notification":
        {"event":"bounce",
         "result":16},
    "Referral":
        {"event":"open",
         "result":10},
    "Positive Notification":
        {"event":"bounce","result":176}
    }
}

But really: why not format the data before you send it, if you have access to the code that outputs this data, change that code to better suite your needs.

Edit:
In response to your comment, I think what you're actually after is this:

var b={result{}};
for (i=0;i<a.result.length;i++)
{
    b.result[a.result[i].category] = b.result[a.result[i].category] || {};//use existing, or create new object
    b.result[a.result[i].category][a.result[i].event] = a.result[i].result;//add property for event type, assign value
}

After this code has run, object b looks like this:

{"result":
    {"Negative Notification":
        {"open":2,
         "delivered":34,
         "processed":34,
         "bounce":16},
      "Referral":
        {"bounce":1,
         "delivered":17,
         "processed":18,
         "open":10},
      "Positive Notification":
         {"open":42,
          "processed":504,
          "delivered":504,
          "bounce":176}
     }
}

That means that, instead of using b.result.Referral[0].bounce, you can use b.result.Referral.bounce. But even more importantly, there's no need for that result property in the first place:

var result ={};
for (i=0;i<a.result.length;i++)
{
    result[a.result[i].category] = result[a.result[i].category] || {};
    result[a.result[i].category][a.result[i].event] = a.result[i].result;
}
console.log(result);

{"Negative Notification":
    {"open":2,
     "delivered":34,
     "processed":34,
     "bounce":16},
  "Referral":
    {"bounce":1,
     "delivered":17,
     "processed":18,
     "open":10},
  "Positive Notification":
     {"open":42,
      "processed":504,
      "delivered":504,
      "bounce":176}
 }

نصائح أخرى

Simply looping through and naming the objects is leading me nowhere.

And yet, that's exactly what you need to do. :-) Probably writing them to a different object.

For instance: Live Example | Source

// The object that will contain the result
var obj = {};

// Loop through the `result` array on your object
yourObject.result.forEach(function(element) {
    // See if we already have an entry for that category
    var entry = obj[element.category];
    if (!entry) {
        // We don't, so create a blank array for it
        entry = obj[element.category] = [];
    }

    // Push this entry in the category's array
    entry.push(element);
});

That's just the bones of it, you'll need to do the bit where you're summing up the bounces and such. But that should get you started...

The above relies on the ES5 forEach function. If you need to support browsers that don't have it, you can apply an "ES5 shim."

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top