Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 以最小的复杂性对数组中的元素进行分组的最佳方法_Javascript_Performance_Algorithm - Fatal编程技术网

Javascript 以最小的复杂性对数组中的元素进行分组的最佳方法

Javascript 以最小的复杂性对数组中的元素进行分组的最佳方法,javascript,performance,algorithm,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","P

我有一个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}]
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"}]}]
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;
}; 
 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,[]);
这个数组包含大约100000条记录。我希望输出按“类型”和“价格”分组。应该是这样的:

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 = [{"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"}]}]
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;
}; 
 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,[]);
这必须在纯javascript中完成,我不能使用像undersore.js这样的库。我试图解决这个问题,但它有3个嵌套for循环,这使得复杂性为n^4。对于这个问题,有什么更好的解决方案

我的函数如下所示:

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 = [{"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"}]}]
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;
}; 
 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,[]);

由于
Type
s和
Price
s在分组后是唯一的,我认为像
{“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;
});
如果无法处理此结构,则可以执行另一个到结构的映射。 当您对数组进行一次迭代时,其复杂性应为n

找一把能用的小提琴


编辑:所以将所有内容重新映射到您的结构。重新映射的顺序远小于第一次映射,因为分组已经完成

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

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

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

下面是另一项努力。这里有一个

为了进行性能测试,我还制作了一个包含163840个元素的模型。在Chrome(OSX)上,原始解决方案比此解决方案慢90%

几点注意:

可以根据您的情况进行优化(例如,对对象克隆进行hasOwnProperty检查)

此外,如果您需要最新的总计作为第一个元素,请使用
unshift
,而不是按将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);

试过这样的方法:

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 = [{"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"}]}]
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;
}; 
 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,[]);

我认为更好的方法是显示您的4嵌套for循环,然后我们可以帮助您优化it@Madedan张贴。。但是Oops3对于循环…这很好,但是我认为我不能改变结构