如何按属性值对coffeescript Map对象进行排序?

时间:2013-12-05 11:49:16

标签: sorting coffeescript

我有一个articles列表,每个列表都有一个简单的字符串数组Tags。我像这样计算标签频率:

计算标记频率

getTags = (articles) ->
    tags= {}
    for article in articles
        for tag in article.Tags
            tags[tag] = (tags[tag] or 0) + 1
    tags

示例结果

生成的tags地图是一个对象,其属性名称设置为Tag名称,属性值设置为频率计数,如下所示:

enter image description here

问题

我想通过属性值(频率计数)来订购此列表,我该如何实现?

注意:我很乐意在需要时更改计数方法

修改1

感谢@LeonidBeschastny我现在有了工作代码:

getTags = (articles) ->
    tags = {}
    for article in articles
        for tag in article.Tags
            tags[tag] = (tags[tag] or 0) + 1

    tags = do (tags) ->
        keys = Object.keys(tags).sort (a, b) -> tags[b] - tags[a]
        {name, count: tags[name]} for name in keys

    tags

您可以看到我必须将未排序的tags地图对象投影到一个新的已排序{name:value}对象数组中。

感觉这是太多的工作,我想也许原始未分类的对象是一个错误。有没有办法在没有经过这个中间步骤的情况下进入排序数组?

修改2

感谢@hpaulj做了一些时间测试并发现上面的代码与其他可能的解决方案(例如运行的有序堆)相比实际上相当有效。

我现在已将此代码投入生产并且运行良好。

2 个答案:

答案 0 :(得分:2)

您可以使用Array::sort对标记进行排序,然后重建tags对象:

tags = do (tags) ->
  res = {}
  keys = Object.keys(tags).sort (a, b) -> tags[b] - tags[a]
  for k in keys
    res[k] = tags[k]
  res

更新

至于插入顺序,mu is too short是正确的,ECMA规范无法保证。 V8为文字(非数字)键维护它,但我对其他JS引擎不太确定。

因此,正确的解决方案是使用数组:

tags = do (tags) ->
  keys = Object.keys(tags).sort (a, b) -> tags[b] - tags[a]
  {name, count: tags[name]} for name in keys

答案 1 :(得分:1)

使用heapq。这比简单计数然后排序更复杂,但如果我们需要一个运行的排序计数可能会很有用。

使用Python的{1},https://github.com/qiao/heap.js

的Coffeescript翻译
heapq

产生

heap = require './heap'
# adapted from 
# http://docs.python.org/2/library/heapq.html#priority-queue-implementation-notes
pq = []                         # list of entries arranged in a heap
entry_finder = {}               # mapping of tasks to entries
REMOVED = '<removed-task>'
counter = [0]

remove_task = (task) ->
    # Mark an existing task as REMOVED.  return null if not found.
    entry = entry_finder[task]
    if entry?
        delete entry_finder[task]
        entry[entry.length-1] = REMOVED
    return entry

count_task = (task) ->
    entry = remove_task(task)
    if entry?
         [priority, count, _] = entry
         priority += 1
    else
        counter[0] += 1
        count = counter[0]
        priority = 1
    entry = [priority, count, task]
    entry_finder[task] = entry
    heap.push(pq, entry)

console.log h = ['one','two','one','three','four','two','one']
for task in h
    count_task(task)
console.log entry_finder
console.log pq

alist = heap.nlargest(pq, 10)
for x in alist
    [priority, count, task] = x
    if task != REMOVED
        console.log task, priority, count