最简单的方法是将数组中的元素分组,复杂度最低

时间:2014-02-05 12:16:48

标签: javascript performance algorithm

我有一个JSON数组,如下所示:

var map_results = [{"Type":"Flat","Price":100.9},
                   {"Type":"Room","Price":23.5},
                   {"Type":"Flat","Price":67.5},
                   {"Type":"Flat","Price":100.9}
                   {"Type":"Plot","Price":89.8}]

此数组包含大约100,000条记录。我希望输出按“类型”和“价格”分组。它应该是这样的:

var expected_output = [{"Type":"Flat", "Data":[{"Price":100.9, "Total":2},
                                               {"Price":67.5,  "Total":1}] },
                       {"Type":"Room","Data":[{"Price":23.5,"Total":1}]},
                       {"Type":"Plot","Data":[{"Price":89.8, "Total:1"}]}]

这必须在纯javascript中完成,我不能使用像undersore.js这样的库。我尝试解决问题,但它有3个嵌套for循环,使复杂性为n ^ 4。什么可以是解决这个问题的更好方法?

我的功能看起来像这样:

var reduce = function (map_results) {
    var results = [];
    for (var i in map_results) {
      var type_found = 0;
      for(var result in results){
         if (map_results[i]["Type"] == results[result]["Type"]){
         type_found = 1;
         var price_found = 0;
         for(var data in results[result]["Data"]){
           if(map_results[i]["Price"] == results[result]["Data"][data]["Price"]){
              price_found = 1;
              results[result]["Data"][data]["Total"] +=1;
            }
          }
          if(price_found == 0){
            results[result]["Data"].push({"Price":map_results[i]["Price"], "Total":1});
          }
        }
      }
      if(type_found == 0){
        results.push({"Type":map_results[i]["Type"], "Data":[{"Price":map_results[i]["Price"],"Total":1}]});
   }
 }
 return results;
}; 

4 个答案:

答案 0 :(得分:0)

由于TypePrice在分组后是唯一的,我认为像{"Flat": {"100.9":2,"67.5":1}, {"Room": {"23.5": 1}}}这样的结构会更容易处理。所以可以通过以下方式进行分组:

var output = {};
map_results.map(function(el, i) {
  output[el["Type"]] = output[el["Type"]] || [];
  output[el["Type"]][el["Price"] = (output[el["Type"]][el["Price"]+1) || 1;
});

如果无法处理此结构,则可以对结构进行另一次映射。 当您迭代Array时,这应该具有n的复杂性。

here找工作小提琴。

<小时/> 编辑:所以重新映射到你的结构。重映射的顺序远远小于第一个映射,因为分组已经完成。

var expected_output = [];
for(type in output) {
  var prices = [];
  for(price in output[type]) {
    prices.push({"Price": price, "Total": output[type][price]);
  }
  expected_output.push({"Type": type, "Data": prices});
}

答案 1 :(得分:0)

我有一个简短的函数来处理所请求功能的第一部分:它将map_results映射到所需的格式:

var map_results = [{"Type":"Flat","Price":100.9},
                   {"Type":"Room","Price":23.5},
                   {"Type":"Flat","Price":67.5},
                   {"Type":"Flat","Price":100.9},
                   {"Type":"Plot","Price":89.8}]

var expected_output = map_results.reduce(function(obj, current){
    if(!obj[current.Type]){
        obj[current.Type] = {'Type':current.Type, 'Data':[]};
    }
    obj[current.Type].Data.push({'Price':current.Price, 'Total':1});
    return obj;
},{})

然后这段代码需要计算总数,我担心:

for(var type in expected_output){
    var d = {};
    for(var item in expected_output[type].Data){
        d[expected_output[type].Data[item].Price] = (d[expected_output[type].Data[item].Price] || 0) + 1;
    }
    expected_output[type].Data = [];
    for(var i in d){
        expected_output[type].Data.push({
            'Price':i,
            'Total':d[i]
        })
    }
}

输出:

{
    "Flat":{
        "Type":"Flat",
        "Data":[{"Price":"100.9","Total":2},
                {"Price":"67.5","Total":1}]
    },
    "Room":{
        "Type":"Room",
        "Data":[{"Price":"23.5","Total":1}]
    },
    "Plot":{
        "Type":"Plot",
        "Data":[{"Price":"89.8","Total":1}]
    }
} 

答案 2 :(得分:0)

以下是另一项努力。这是一个FIDDLE

对于性能测试,我还模拟了一个带有163840个元素的JSPerf test。在Chrome(OSX)上,原始解决方案比这个解决方案慢90%。

很少注意到:

随意针对您的情况进行优化(例如,取出对象克隆的hasOwnProperty检查)。

此外,如果您需要使用最新的Total作为第一个元素,请使用unshift而不是push来将obj添加到数组的开头。

function groupBy(arr, key, key2) {
  var retArr = [];
  arr.reduce(function(previousValue, currentValue, index, array){
   if(currentValue.hasOwnProperty(key)) {
       var kVal = currentValue[key];
       if(!previousValue.hasOwnProperty(kVal)) {
           previousValue[kVal] = {};
           retArr.push(previousValue[kVal]);
           previousValue[kVal][key] = kVal;
           previousValue[kVal]["Data"] = [];
       }
       var prevNode = previousValue[kVal];
       if(currentValue.hasOwnProperty(key2)) {
           var obj = {};
           for(var k in currentValue) {
               if(currentValue.hasOwnProperty(k) && k!=key)
                   obj[k] = currentValue[k];
           }
           obj["Total"] = prevNode["Data"].length + 1;
           prevNode["Data"].push(obj);   
       }
    }
    return previousValue;
  }, {});
  return retArr;
}


var map_results = [{"Type":"Flat","Price":100.9},
               {"Type":"Room","Price":23.5},
               {"Type":"Flat","Price":67.5},
               {"Type":"Flat","Price":100.9},
               {"Type":"Plot","Price":89.8}];
var expected_output = groupBy(map_results, "Type", "Price");
console.dir(expected_output);

答案 3 :(得分:0)

尝试过这样的事情:

 var reduce_func = function (previous, current) {

                        if(previous.length == 0){
                          previous.push({Type: current.Type, Data:[{Price:current.Price,Total:1}]});
                          return previous;
                        }
                        var type_found = 0;
                        for (var one in previous) {
                            if (current.Type == previous[one].Type){
                                type_found = 1;
                                var price_found = 0;
                                for(var data in previous[one].Data){
                                    if(current.Price == previous[one].Data[data].Price){
                                        price_found = 1;
                                        previous[one].Data[data].Total += 1;
                                    }
                                }
                                if(price_found == 0){
                                    previous[one].Data.push({Price:current.Price, Total:1});
                                }
                            }
                        }
                        if(type_found == 0){
                            previous.push({Type:current.Type, Data:[{Price : current.Price ,Total:1}]});
                        }
                        return previous;
                      }

map_results.reduce(reduce_func,[]);