<repo_name>cherrry/static-tag-cloud<file_sep>/src/tag-cloud.js
'use strict'

var assign = require('object-assign')

var distribute = require('./distribute')
var shuffle = require('./shuffle')

var DEFAULT_NUM_OF_BUCKETS = 4

var tagCloud = function (tagDefs, options) {
  // only process tags with positive count
  tagDefs = tagDefs.filter(function (tagDef) { return tagDef.count > 0 })
  if (tagDefs.length === 0) return []

  var numOfBuckets = (options && options.numOfBuckets) || DEFAULT_NUM_OF_BUCKETS
  return shuffle(distribute(tagDefs, numOfBuckets))
}

module.exports = tagCloud
<file_sep>/src/shuffle.js
'use strict'

var stringHash = require('./string-hash')
var tagHash = require('./tag-hash')
var tagKey = require('./tag-key')

// A pseudo-random shuffle function
var shuffle = function (tagDefs) {
  // special M for hashing tagDefs
  var M = stringHash(tagDefs.map(tagKey).sort().join(','))

  var tagDefsByHashKey = {}
  tagDefs.forEach(function (tagDef) {
    var hashKey = tagHash(tagDef, M)
    tagDefsByHashKey[hashKey] = tagDef
  })

  return Object.keys(tagDefsByHashKey)
    .filter(function (key) { return tagDefsByHashKey.hasOwnProperty(key) })
    .sort()
    .map(function (hashKey) {
      return tagDefsByHashKey[hashKey]
    })
}

module.exports = shuffle
<file_sep>/src/string-hash.js
'use strict'

// Two carefully selected primes
var P1 = 43063
var P2 = 48611

var stringHash = function (string, p1, p2) {
  p1 = p1 || P1
  p2 = p2 || P2

  var hash = 0
  for (var i = 0, _len = string.length; i < _len; i++) {
    var code = string.charCodeAt(i)
    hash = ((((hash * p1) % p2) + code) * p1) % p2
  }
  return hash
}

module.exports = stringHash
<file_sep>/README.md
# Static Tag Cloud

A deterministic tag cloud generator which always gives the same output upon the same input.

## Install

```sh
$ npm install --save static-tag-cloud
```

## Usage

```js
import tagCloud from 'static-tag-cloud'

tagCloud([
  { id: 'apple', count: 1 },
  { id: 'boy', count: 2 },
  { id: 'cat', count: 4 },
  { id: 'dog', count: 1 }
])

/*
[
  { bucket: 2, id: 'apple', count: 1 },
  { bucket: 2, id: 'dog', count: 1 },
  { bucket: 2, id: 'boy', count: 2 },
  { bucket: 4, id: 'cat', count: 4 }
]
*/
```
<file_sep>/src/distribute.js
'use strict'

var assign = require('object-assign')

// assign bucket number
var distribute = function (tagDefs, numOfBuckets) {
  var length = tagDefs.length
  var counts = tagDefs.map(function (tagDef) {
    return tagDef.count
  }).sort()

  var step = tagDefs.length / numOfBuckets
  var upperBounds = []
  for (var i = 0; i < numOfBuckets; i++) {
    var from = Math.min(length - 1, Math.max(0, Math.round(i * step)))
    upperBounds.push(counts[from])
  }

  var findBucket = function (count) {
    if (count === counts[0]) {
      return 1
    }

    var bucket = 1
    while (bucket < numOfBuckets && count >= upperBounds[bucket]) {
      bucket++
    }
    return bucket
  }

  return tagDefs.map(function (tagDef) {
    return assign({ bucket: findBucket(tagDef.count) }, tagDef)
  })
}

module.exports = distribute
<file_sep>/src/tag-hash.js
'use strict'

var stringHash = require('./string-hash')
var tagKey = require('./tag-key')

var tagHash = function (tagDef, P1, P2) {
  return stringHash(tagKey(tagDef), P1, P2)
}

module.exports = tagHash
<file_sep>/index.js
var tagCloud = require('./src/tag-cloud')

module.exports = tagCloud
