通过node.js中的多个字段对JSON进行分组

时间:2015-07-22 09:21:26

标签: javascript json node.js

我有一个JSON如下。

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"

}]

我想通过'id'和'city'将这个JSON分组。新形成的分组JSON应如下所示。

[
    {
        "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"
                    }
                }
            ]
        }
    }
]

我尝试了循环和排序,但我无法获得所需的结果。有没有办法构建这个JSON。

3 个答案:

答案 0 :(得分:2)

以下是通过多个字段进行分组的一种方法:

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);
}());

我已经概括了其余属性的包含,因此不必明确定义它们(根据评论中OP的要求)。

我试图避免使用for循环,以便从解决方案中提取迭代细节。我还采用了函数式编程方法,并试图避免创建带副作用的函数。在加载到jsbin的解决方案中,我添加了一个用于数组包含的polyfill,它有望进入ECMAScript版本7(预计在2016年)。在Javascript中有一个有用的"功能编程"可能有帮助的教程:

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

此答案的可能扩展是对分组数据进行排序,以匹配OP列出的示例输出。

答案 1 :(得分:1)

我不知道这是不是你想要的。

	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);

如果有帮助,请告诉我。

已更改以符合您的说明:

	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 :(得分:0)

我认为这将是一个更好的解决方案:

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;
}