3

I've a JSON as follows.

var test = [{

   "id": "3",
   "city": "seattle",
   "place" : "xxx",
   "usage" : "163612",
   "available": "162500"

}, {

   "id": "4",
   "city": "washington",
   "place" : "xxx",
   "usage" : "52542",
   "available": "86624"

}, {

   "id": "3",
   "city": "seattle",
   "place" : "yyy",
   "usage" : "163612",
   "available": "962500"

},
{

   "id": "5",
   "city": "seattle",
   "place" : "yyy",
   "usage" : "562",
   "available": "24252"
},
{

   "id": "4",
   "city": "washington",
   "place" : "yyy",
   "usage" : "163612",
   "available": "319250"

}]

I want to group this JSON by 'id' and 'city'. The newly formed grouped JSON should be as follows.

[
    {
        "3": {
            "seattle": [
                {
                    "xxx": {
                        "usage": "163612",
                        "available": "162500"
                    }
                },
                {
                    "yyy": {
                        "usage": "163612",
                        "available": "962500"
                    }
                }
            ]
        }
    },
    {
        "4": {
            "washington": [
                {
                    "xxx": {
                        "usage": "52542",
                        "available": "86624"
                    }
                },
                {
                    "yyy": {
                        "usage": "163612",
                        "available": "319250"
                    }
                }
            ]
        }
    },
    {
        "5": {
            "seattle": [
                {
                    "xxx": {
                        "usage": "562",
                        "available": "24252"
                    }
                }
            ]
        }
    }
]

I tried with looping and sorting and I'm unable to get the required result. Is there any way to construct this JSON.

1
  • 1
    Its a JS object, not a JSON. Commented Jul 22, 2015 at 9:26

3 Answers 3

2

Here is one way to do the grouping by multiple fields:

http://jsbin.com/xixemo/edit?js,console

(function () {
  "use strict";

  var test = [{

   "id": "3",
   "city": "seattle",
   "place" : "xxx",
   "usage" : "163612",
   "available": "162500"

}, {

   "id": "4",
   "city": "washington",
   "place" : "xxx",
   "usage" : "52542",
   "available": "86624"

}, {

   "id": "3",
   "city": "seattle",
   "place" : "yyy",
   "usage" : "163612",
   "available": "962500"

},
{

   "id": "5",
   "city": "seattle",
   "place" : "yyy",
   "usage" : "562",
   "available": "24252"
},
{

   "id": "4",
   "city": "washington",
   "place" : "yyy",
   "usage" : "163612",
   "available": "319250"

}],
      getRemainingProperties = function (obj, propertiesToExclude) {
        return Object.keys(obj)
          .filter(function (key) {
            return !propertiesToExclude.includes(key);
          })  
          .reduce(function (acc, curr) {
            var result = {};

            if (!acc) {
              result[curr] = obj[curr];
              return result;
            }
            result = acc;
            result[curr] = obj[curr];
            return result;          
          }, undefined);
      },
      excludedProperties = ["id", "city", "place"],
      transformCity = function (cityInformation) {
        var id = {},        
            city = {},
            place = {},
            remainder = getRemainingProperties(cityInformation, excludedProperties);
        place[cityInformation.place] = remainder;
        city[cityInformation.city] = [place];
        id[cityInformation.id] = city;
        return id;
      },
      initialReduceUndefinedValue,
      idExists = function (searchArray, id) {
        return searchArray.reduce(function (acc, curr) {
          if (!acc){
            return curr.hasOwnProperty(id);
          }
          return true;
        }, undefined);
      },
      lift = function (array) {
        //returns an object from inside container array without using array index
        if (!Array.isArray(array)) {
          return array;
        }
        return array.reduce(function (acc, curr) {
          return curr;
        });
      },
      answer = test.reduce(function (acc, curr) {
        var result, 
            matchingId, //create a new object that will have appended properties for the current city
            missingPlace = {};

        if (!acc) {
          return [transformCity(curr)];
        }
        if (idExists(acc, curr.id)) {
          result = acc.filter(function (obj) {
            //store the unmodified objects to return
            return !obj.hasOwnProperty(curr.id);
          });
          matchingId = lift(acc.filter(function (obj) {
            return obj.hasOwnProperty(curr.id);
          }));

          if (!matchingId[curr.id].hasOwnProperty(curr.city)) {
            //if the object does not have the city, then add the city
            matchingId[curr.city] = {};
          }
          if (!matchingId[curr.id][curr.city].hasOwnProperty(curr.place)) {
            //if the object does not have the place, then add the place            
            missingPlace[curr.place] = getRemainingProperties(curr, excludedProperties);
            matchingId[curr.id][curr.city].push(missingPlace);
          }
          result.push(matchingId);//add here just incase a city is duplicated
          return result;
        } else {//unique city id found, add new city
          acc.push(transformCity(curr));
        }
        return acc;
      }, initialReduceUndefinedValue);

  console.log(answer);
}());

I have generalised the inclusion of the remaining properties so that they do not have to be explicitly defined (as requested by the OP in a comment).

I tried to avoid using for loops so that the iteration detail is extracted out of the solution. I also took a functional programming approach and tried to avoid creating functions with side effects. In the solution loaded into jsbin I added a polyfill for array includes which will hopefully make it into ECMAScript version 7 (expected in 2016). There is a useful "Functional Programming in Javascript" tutorial that may help:

http://jhusain.github.io/learnrx/

A possible extension to this answer is to sort the grouped data to match the example output listed by the OP.

Sign up to request clarification or add additional context in comments.

Comments

1

I don't know if that was what you were looking for.

	var newObj = [];

	for (var i in test) {
		var cityObj = test[i];

		var newItem = {};

		var foundItem = false;
		for (var j in newObj) {
			var existingItem = newObj[j];
			if (newObj[j].hasOwnProperty(cityObj.id)) {
				foundItem = j;
			}
		}

		if (!foundItem) {
			newItem[cityObj.id] = {};
			newItem[cityObj.id][cityObj.city] = {};
			newItem[cityObj.id][cityObj.city][cityObj.place] = { usage: cityObj.usage, available: cityObj.available };
			newObj.push(newItem);
		} else {
			newObj[foundItem][cityObj.id][cityObj.city][cityObj.place] = { usage: cityObj.usage, available: cityObj.available };
		}
	}

	console.dir(newObj);

Please let me know if this helped.

Changed to match your description:

	var newObj = [];

	for (var i in test) {
		var cityObj = test[i];

		var newItem = {};

		var foundItem = false;
		for (var j in newObj) {
			var existingItem = newObj[j];
			if (newObj[j].hasOwnProperty(cityObj.id)) {
				foundItem = j;
			}
		}

		if (!foundItem) {
			newItem[cityObj.id] = {};
			newItem[cityObj.id][cityObj.city] = [];
			var place = {};
			place[cityObj.place] = { usage: cityObj.usage, available: cityObj.available };
			newItem[cityObj.id][cityObj.city].push(place);
			newObj.push(newItem);
		} else {
			var place = {};
			place[cityObj.place] = { usage: cityObj.usage, available: cityObj.available };
			newObj[foundItem][cityObj.id][cityObj.city].push(place);
		}
	}

	console.dir(newObj);

2 Comments

This is really close to what I want. But the city part in grouped JSON should be an array of JSON. Also this line "newItem[cityObj.id][cityObj.city][cityObj.place] = { usage: cityObj.usage, available: cityObj.available };" is like hardcoding the remaining attributes in JSON. Can we not form this by taking all the attributes except id, city and place in the source JSON.
@NagaLakshmi If you want to not hardcode properties like usage and available you need to hardcode the properties that you do not want to include in object that is pushed into city array and when iterating over properties of cityObj object you just exclude them. But I feel that it is still the same job just done in a diffrent way. Check the updated answer above and post me back if it was helpful in any way.
0

I think that this will be a better a solution :

function group(data, column) {
  var generatedData = {};
  for (var i in data){
    var dt = data[i];
    var key = dt[column];
    if (!(key in generatedData)) {
      generatedData[key] = [];
    }
    generatedData[key].push(dt);
  }
  return generatedData;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.