[英]Frequency Sort Array Of Objects in JS and if frequency Matches sort on basis of object property
I have Array of 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"
}
}
]
I want to frequency Sort the array so that i get like [object:count]
我想频率排序数组,以便我得到像
[object:count]
How do i get the array to Transform arr to the format: 如何将数组转换为以下格式:
// [{key: x, count: 3, price: 5}},{key: y:, count: 2, price: 3}
[{x:3},{y:2},{z:2},{q:1}]
But the problem i am facing is if the frequency matches then the sort has to check the object's property i:e in this case the price and if the price is more than the other matching element that should be given weight age so in this case z price is more than y so z should be given priority. 但我面临的问题是如果频率匹配那么排序必须检查对象的属性i:e在这种情况下价格和价格是否超过应该给予权重年龄的其他匹配元素所以在这种情况下z价格超过y因此z应优先考虑。
[{x:3},{z:2},{y:2},{q:1}]
This is what i have tried so far: 这是我到目前为止所尝试的:
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%}
Update after answer 回答后更新
I still donot know how to transform the array to this format 我仍然不知道如何将数组转换为这种格式
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>
now it works but how to get the object to this format from array 现在它可以工作,但如何从数组中获取此格式的对象
You could do it with the following ES6 code: 您可以使用以下ES6代码执行此操作:
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);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The code builds a Map
to ensure one entry per key. 代码构建一个
Map
以确保每个键一个条目。 It is created with reduce
, which gets as starting value new Map
and which is then referenced as acc
. 它是用
reduce
创建的,它以new Map
作为起始值,然后作为acc
引用。
reduce
will iterate over the input array a
, and for each entry, it will extract the key
with Object.keys
. reduce
将遍历输入数组a
,对于每个条目,它将使用Object.keys
提取key
。 Since there is (and should be) only one key per object, it is extracted with [0]
from the resulting array of keys. 由于每个对象只有(并且应该)只有一个键,因此从得到的键数组中用
[0]
提取它。
Then with acc.get
it is verified whether we already have an entry for that key. 然后使用
acc.get
验证我们是否已经拥有该密钥的条目。 If so, obj
is set to the object we had previously stored for that key. 如果是这样,则将
obj
设置为我们先前为该键存储的对象。 If not -- and this is the case in the first iteration -- a new object is created with key
and count
properties which get the correct values, and this object is merged with the deeper object in the input array ( o[key]
). 如果不是 - 并且在第一次迭代中就是这种情况 - 使用
key
和count
属性创建一个新对象,该属性获取正确的值,并且此对象与输入数组中的更深对象合并( o[key]
) 。 Practically this means the price
key and value are added to the object that already has key
and count
. 实际上,这意味着
price
键和值被添加到已经具有key
和count
的对象中。
In either case (whether we created a new object or retrieved it from the Map), its count property is incremented. 在任何一种情况下(无论我们是创建新对象还是从Map中检索它),其count属性都会递增。
Then this object is stored in the Map at the corresponding key with acc.set(key, obj)
. 然后使用
acc.set(key, obj)
将此对象存储在Map的相应键中。 This is returned to the reduce
internals (ie the updated acc
is returned), and this will be the value of acc
in the next iteration, as that is how reduce
works. 这将返回到
reduce
内部(即返回更新的acc
),这将是下一次迭代中acc
的值,因为reduce
是如何工作的。
After the final iteration reduce
will return the completed Map. 最后一次迭代后,
reduce
将返回完成的Map。 This is then converted to an array with Array.from
. 然后将其转换为
Array.from
数组。 During its execution we transform each entry, because by default a Map entry will be converted to a key/value pair (array), but we only want to keep the value (since it now contains the key
property). 在执行期间,我们转换每个条目,因为默认情况下,Map条目将转换为键/值对(数组),但我们只想保留该值(因为它现在包含
key
属性)。 So that is what happens in the callback argument provided to Array.from
: 这就是为
Array.from
提供的回调参数中发生的情况:
([key, obj]) => obj
Now we have an array of objects, where each object has the three desired properties. 现在我们有一个对象数组,其中每个对象都有三个所需的属性。 The only thing remaining is the sorting.
唯一剩下的就是排序。
For that we subtract the counts of the two objects that are being compared (just as you already had done). 为此,我们减去要比较的两个对象的计数(就像您已经完成的那样)。 However, when they are equal, we need to do more.
但是,当他们平等时,我们需要做更多。 In that case the difference is zero, which is falsy, and so with a boolean
||
在这种情况下,差异为零,这是假的,因此使用布尔值
||
we force JavaScript to evaluate what follows after it. 我们强制JavaScript来评估它之后的内容。 In that case we sort by the price, again by subtracting the prices from each other.
在这种情况下,我们按价格排序,再次减去彼此的价格。 Note that your prices are strings, but the subtraction operator will convert them to numbers on the fly.
请注意,您的价格是字符串,但减法运算符会将它们转换为动态数字。
Try this one using reduce
and then sort
. 尝试使用
reduce
然后sort
。
Explaination 释
In order to achieve the desired result you can break down the task in below steps. 为了达到预期的效果,您可以按以下步骤分解任务。
1. Grouping - Group by the items in the array using the Property names (x,y,z) 1.分组 - 使用属性名称(x,y,z)按数组中的项目分组
2. Sorting - Sort the Result from Step1 in descending order where the first criteria is the count of items and the second criteria is the price. 2.排序 - 按降序对Step1的结果进行排序,其中第一个标准是项目数,第二个标准是价格。
1.Grouping - There isn't a native group by
function in javascript. 1.分组 - javascript中没有
group by
功能划分的本group by
。 Therefore, we can make use of the reduce
function which basically runs a function over the sequence of array and also returns the accumulated value. 因此,我们可以使用基本上在数组序列上运行函数的
reduce
函数,并返回累加值。
a. 一个。 In the reduce function, the accumulator will start with an empty array as mentioned in comments in code towards the end of the reduce function.
在reduce函数中,累加器将以一个空数组开始,如代码中的注释所述,在reduce函数结束时。
b. 湾 We get the property name like "x", "y", "z" by traversing the object.
我们通过遍历对象获得属性名称,如“x”,“y”,“z”。 Also, we use the zeroth index as there is only one property like "x", "y", "z".
此外,我们使用第零索引,因为只有一个属性,如“x”,“y”,“z”。
c. C。 After that we check if the property is already in the Array or not.
之后,我们检查属性是否已经在数组中。
d. d。 If the Property is not in the array, then we need to add the property to the Array.
如果属性不在数组中,那么我们需要将属性添加到数组中。
e. 即 We create a object to handle the count and price information which would be used later.
我们创建一个对象来处理稍后将使用的计数和价格信息。
f. F。 If the Property already exists as mentioned in step c, then we need to increment the count of that property
elementInArray[propName].count++;
如果属性已经存在,如步骤c中所述,那么我们需要增加该属性的计数
elementInArray[propName].count++;
2. Sorting 2.排序
a. 一个。
sort
function takes a comparer function. sort
函数采用比较器函数。 In that function we compare the 2 items firsly by their count
. 在该功能中,我们首先按照
count
比较2项。 If count
is equal then we compare them by the price
. 如果
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);
I'm just giving you the skeleton, but the _.sortBy
method in lodash or underscore will perform a stable sort (ie preserve previous sorts) and let you apply multiple sorts. 我只是给你骨架,但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 */)
}
Just an Update to the Comment of John On Agalo Answer 只是John On Agalo答案评论的更新
Mapping each of the object keys into new object 将每个对象键映射到新对象
In the Else block you can write this 在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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.