简体   繁体   English

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

[英]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; } 

Explanation 说明

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] ). 如果不是 - 并且在第一次迭代中就是这种情况 - 使用keycount属性创建一个新对象,该属性获取正确的值,并且此对象与输入数组中的更深对象合并( o[key] ) 。 Practically this means the price key and value are added to the object that already has key and count . 实际上,这意味着price键和值被添加到已经具有keycount的对象中。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM