JS中的频率排序对象数组和频率匹配基于对象属性排序

时间:2017-06-21 15:29:44

标签: javascript arrays sorting javascript-objects

我有对象数组

[
    {"X" : {
        "price" : "5"
      }
    },
    {"Y" : {
        "price" : "3"
      }
    },
    {"Y" : {
        "price" : "3"
      }
    },
    {"Z" : {
        "price" : "4"
      }
    },
    {"Q" : {
        "price" : "2"
      }
    },
    {"X" : {
        "price" : "5"
      }
    },
    {"Z" : {
        "price" : "4"
      }
    },
    {"X" : {
        "price" : "5"
      }
    }
]

我想对数组进行频率排序,以便得到[object:count]

如何将数组转换为以下格式:

// [{key: x, count: 3, price: 5}},{key: y:, count: 2, price: 3}

[{x:3},{y:2},{z:2},{q:1}]

但我遇到的问题是,如果频率匹配,则排序必须检查对象的属性i:e在这种情况下价格和价格是否超过应该给出的其他匹配元素权重年龄所以在这种情况下,z价格大于y,所以z应该优先考虑。

[{x:3},{z:2},{y:2},{q:1}]

这是我到目前为止所尝试的:



var a = ["x", "v"], b = ["x", "y"], c = ["d", "y"];
var d = a.concat(b, c);
    
function sortByFrequency(array) {
    var frequency = {};
    
    array.forEach(function(value) { frequency[value] = 0; });
  
    var uniques = array.filter(function(value) {
        return ++frequency[value] == 1;
    });
    
    return uniques.sort(function(a, b) {
        return frequency[b] - frequency[a];
    });
}
    
var frequency = sortByFrequency(d);
    
console.log(frequency);

.as-console-wrapper{min-height:100%}




回答后更新

我仍然不知道如何将数组转换为这种格式



   var arr = [
{"key":"X",
	"price" : "5",
	"count" :"3"
  }
,
{"key":"Y",
	"price" : "3",
	"count" :"2"
  }
,
{"key":"Z",
	"price" : "4",
	"count" : "2"
  }

];

var r = _.sortBy(_.sortBy(arr, 'price'), 'count'); 

console.log(JSON.stringify(r));

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
&#13;
&#13;
&#13;

现在它可以工作,但如何从数组

获取此格式的对象

4 个答案:

答案 0 :(得分:2)

您可以使用以下ES6代码执行此操作:

&#13;
&#13;
function sortByFrequency(a) {
    return Array.from(
        a.reduce( (acc, o) => {
            const key = Object.keys(o)[0];
            const obj = acc.get(key) || Object.assign({ key, count: 0 }, o[key]);
            obj.count++;
            return acc.set(key, obj);
        }, new Map),
        ([key, obj]) => obj
    ).sort( (a, b) => b.count - a.count || b.price - a.price );
}

// Sample input
const a = [{
    X: {
        price: "5"
    }
}, {
    Y: {
        price: "3"
    }
}, {
    Y: {
        price: "3"
    }
}, {
    Z: {
        price: "4"
    }
}, {
    Q: {
        price: "2"
    }
}, {
    X: {
        price: "5"
    }
}, {
    Z: {
        price: "4"
    }
}, {
    X: {
        price: "5"
    }
}];

// Perform transformation & output
const res = sortByFrequency(a);
console.log(res);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

解释

代码构建Map以确保每个键一个条目。它使用reduce创建,其作为起始值new Map,然后引用为acc

reduce将遍历输入数组a,对于每个条目,它将使用key提取Object.keys。由于每个对象只有(并且应该)只有一个键,因此从结果数组中用[0]提取它。

然后使用acc.get验证我们是否已经拥有该密钥的条目。如果是,则将obj设置为我们先前为该密钥存储的对象。如果不是 - 在第一次迭代中就是这种情况 - 创建一个新对象,其中keycount属性可以获得正确的值,并且此对象与更深层对象合并。输入数组(o[key])。实际上,这意味着price键和值将添加到已有keycount的对象中。

在任何一种情况下(无论我们是创建新对象还是从Map中检索它),其count属性都会递增。

然后,此对象将acc.set(key, obj)存储在相应键的Map中。这将返回到reduce内部(即返回更新的acc),这将是下一次迭代中acc的值,因为这是reduce的方式的工作原理。

在最后一次迭代后reduce将返回完成的Map。然后将其转换为Array.from的数组。在执行期间,我们转换每个条目,因为默认情况下,Map条目将转换为键/值对(数组),但我们只想保留该值(因为它现在包含key属性)。这就是提供给Array.from的回调参数中发生的事情:

([key, obj]) => obj

现在我们有了一个对象数组,其中每个对象都有三个所需的属性。唯一剩下的就是排序。

为此,我们减去正在比较的两个对象的计数(就像您已经完成的那样)。但是,当他们平等时,我们需要做更多。在这种情况下,差异为零,这是假的,因此使用布尔值||,我们强制JavaScript评估后面的内容。在这种情况下,我们按价格排序,再次减去彼此的价格。请注意,您的价格是字符串,但减法运算符会将它们转换为动态数字。

答案 1 :(得分:1)

使用reduce然后sort尝试此操作。

<强>释

为了达到预期效果,您可以按以下步骤分解任务。

<强> 1。分组 - 使用属性名称(x,y,z)分组数组中的项目 的 2。排序 - 按降序对Step1的结果进行排序,其中第一个标准是项目数,第二个标准是价格。

1.分组 - javascript中没有原生group by功能。因此,我们可以使用基本上在数组序列上运行函数的reduce函数,并返回累加值。

一个。在reduce函数中,累加器将以一个空数组开始,如代码中的注释所述,在reduce函数结束时。

湾我们通过遍历对象获得属性名称,如“x”,“y”,“z”。此外,我们使用第零索引,因为只有一个属性,如“x”,“y”,“z”。

℃。之后,我们检查属性是否已经在数组中。

d。如果属性不在数组中,那么我们需要将属性添加到数组中。

即我们创建一个对象来处理稍后将使用的计数和价格信息。

F。如果属性已经存在,如步骤c中所述,那么我们需要增加该属性的计数elementInArray[propName].count++;

<强> 2。排序
    一个。 sort函数采用比较器函数。在该函数中,我们首先通过count比较2个项目。如果count相等,那么我们会将它们与price进行比较。

var arr = [
    {"X" : {
        "price" : "5"
      }
    },
    {"Y" : {
        "price" : "3"
      }
    },
    {"Y" : {
        "price" : "3"
      }
    },
    {"Z" : {
        "price" : "4"
      }
    },
    {"Q" : {
        "price" : "2"
      }
    },
    {"X" : {
        "price" : "5"
      }
    },
    {"Z" : {
        "price" : "4"
      }
    },
    {"X" : {
        "price" : "5"
      }
    }
];

var frequency = arr.reduce(function (accumulatorObject, currentValue) { 
  var propName = Object.keys(currentValue)[0];
  var elementInArray = accumulatorObject.find((element) => Object.keys(element)[0] === propName);
  if (elementInArray) {
    elementInArray[propName].count++;
  }
  else {
    var newObject = {};
	newObject[propName]  = {};
	newObject[propName].count = 1;
	newObject[propName].price = +currentValue[propName].price;
	accumulatorObject.push(newObject);
 }
  return accumulatorObject;
}, []); //  // Accumulator starts with Empty array. 



frequency.sort(function(first,second){ 
    var diff =  second[Object.keys(second)].count - first[Object.keys(first)].count;
	if( diff === 0) {
		return second[Object.keys(second)].price - first[Object.keys(first)].price;
	}
return diff;});
console.log(frequency);

答案 2 :(得分:0)

我只是给你骨架,但是lodash或下划线中的_.sortBy方法将执行稳定的排序(即保留以前的排序)并让你应用多种排序。

function sortByFrequency(arr) {
    // Transform arr to the format: 
    // [{key: x, count: 3, price: 5}},{key: y:, count: 2, price: 3}, ... 

    arr = _.sortBy(_.sortBy(arr, 'price'), 'count'); 
    // in lodash, this is arr = _.sortBy(arr, ['price', 'count'])

    // transform arr into the format that you want 
    return arr.map(x => /* function */)

}

答案 3 :(得分:0)

John On Agalo答案的评论更新

将每个对象键映射到新对象

在Else区块中,您可以写这个

 var newObject = {};  
    newObject[propName]  = {};
    newObject[propName].count = 1;
  Object.assign(newObject[propName], currentValue[propName]); // this line should do the trick also you need not convert proce to number as while sorting it is done at runtime .
    //newObject[propName].price = +currentValue[propName].price;
  accumulatorObject.push(newObject);