3

I want to count the number of occurrences of strings with node.js and started to implement a dictionary like below:

var dict = {}

// Example content ("string" = count)
dict["alpha"] = 12
dict["beta"] = 39

// Increment count (working)
dict["alpha"]++

But how do I sort this dictionary from highest to lowest count? Is there any built in sorting or do I have to implement this myself?

I want to list the output from highest to lowest count in a file, with each line being:

<count> <string>
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
norq
  • 1,404
  • 2
  • 18
  • 35
  • You cannot create an Ordered object. What is the expected result? – thefourtheye May 08 '15 at 20:11
  • I want to list the output from highest to lowest count in a file, with each line: – norq May 08 '15 at 20:12
  • 2
    Objects are unordered! Use an array for order! – tymeJV May 08 '15 at 20:12
  • you have to convert the object to an array, sort the array, then redefine the object properties from the sorted array, which if the keys are not numerical, will preserve insertion order, contrary to popular opinion and technical specification ambiguity. if someone can show a snip of non-numerical property order not being preserved, i would love to see it. – dandavis May 08 '15 at 20:16
  • http://ejohn.org/blog/javascript-in-chrome/ (for loop order) – dandavis May 08 '15 at 20:23
  • @dandavis Just because it works now doesn't make it safe. – loganfsmyth May 08 '15 at 23:32
  • @loganfsmyth: in theory, but the plan for the future is more of the same, so i think it's a safe bet that things won't temporarily change before ES6 rolls out v8's order behavior platform-wide. – dandavis May 08 '15 at 23:38
  • Does ES6 specify key ordering behavior? I feel like if you are using ES6, you're better off using a Map anyway. – loganfsmyth May 09 '15 at 17:04

3 Answers3

3

Objects are unordered, since the keys are stored based on the hash values. So, there is no such thing called sorting an Object.

But, you can simply sort the keys, based on the count, and apply forEach directly on it, like this

> Object.keys(dict).sort(function(key1, key2) {
...     return dict[key2] - dict[key1];
... }).forEach(function(currentKey) {
...     console.log(currentKey, dict[currentKey]);
... });
beta 39
alpha 12

To understand this, step-by-step you can convert the object to an array of pairs, like this

> var dict = {}
undefined
> dict["alpha"] = 12
12
> dict["beta"] = 39
39
> var temp = Object.keys(dict).map(function(currentKey) {
...     return [currentKey, dict[currentKey]];
... });
undefined
> temp
[ [ 'alpha', 12 ],
  [ 'beta', 39 ] ]

and then sort them based on the second element, with Array.prototype.sort, like this

> temp.sort(function(pair1, pair2) {
...     return pair2[1] - pair1[1];
... });
[ [ 'beta', 39 ],
  [ 'alpha', 12 ] ]

And then print them as you like, like this

> temp.forEach(function(currentPair) {
...     console.log(currentPair[0], currentPair[1]);
... })
beta 39
alpha 12

Since you want to write the result to the file, you can do it like this

> var fileWriter = require("fs").createWriteStream('Output.txt');
undefined
> Object.keys(dict).sort(function (key1, key2) {
...     return dict[key2] - dict[key1];
... }).forEach(function (currentKey) {
...     fileWriter.write(currentKey + " " + dict[currentKey] + "\n");
... });
undefined
> fileWriter.end();
undefined
> require("fs").readFileSync("Output.txt").toString()
'beta 39\nalpha 12\n'

You might want to go through this amazing answer, to better understand how sorting is done in JavaScript.

Community
  • 1
  • 1
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • 2
    can you demonstrate your contention that objects property order is not preserved? i cannot seem to reproduce the behavior you describe despite trying many browsers and node.js... any ideas? – dandavis May 08 '15 at 20:19
  • @dandavis I am not sure, if I understood your question properly. – thefourtheye May 08 '15 at 20:21
  • @dandavis Try `Object.keys({"2": 1, "1": 1});` – thefourtheye May 08 '15 at 20:26
  • @dandavis: The original ECMAScript specification all the way up to ES5 explicitly specify that there is no object property order - that is, you can never expect it to have any meaningful order. It can be random for all you know. However, all modern browsers implement the same key order - first by integers then by insertion order (the last browser that didn't do this was Chrome, but they "fixed" it about a year ago). Programmers have accidentally discovered this similarity in browser implementation and code have been written to rely on it. So now, ES6 explicitly specify object property order. – slebetman May 08 '15 at 20:30
  • that's not the same thing now is it? i'm talking about "alpha" and "beta", as i noted in the comment above that numerical keys are sorted instead of insertion... i know what the spec says and what everyone thinks, but i've not yet been able to confirm it in code. if it's not true in the real world, then the spec doesn't matter much outside of conjecture and pedantic theology. there's nice uses for key order to, like formatting json API responses... – dandavis May 08 '15 at 20:30
  • 2
    @dandavis: In my opinion the ES6 specification did the wrong thing - encourage buggy code instead of discouraging it. In my own code I still treat object key order as "random". Code that depends on implementation details rather than language feature are buggy. – slebetman May 08 '15 at 20:31
  • @dandavis Think of it like this, how would you order a hash table? – thefourtheye May 08 '15 at 20:32
  • @dandavis: There is still inconsistencies with `Object.keys`. The excuse the ES6 committee had in not specifying it is that it's an array and you can simply call `.sort()` if you want. The behavior is only specified for `for...in` loops – slebetman May 08 '15 at 20:33
  • V8 doesn't use a hash table for all objects, only some of them, so they are not 1:1 and bg implementation details are beyond the scope of what we can observe from userland anyway... i'm not saying your wrong, and belive me i've heard it before, i just can't prove it, which makes me skeptical at best. – dandavis May 08 '15 at 20:34
  • @dandavis: Brendan Eich recommends that ES7 explicitly specifies how `Object.keys()` order work if all browsers end up doing the same thing in the future. But that's 10 years or so from now – slebetman May 08 '15 at 20:35
  • @slebetman: it would be good to get clarification on this. let's hope core makers continue to honor the defacto standard until then because ordered keys can be useful if you know how to use them, and i think we should, even if we don't rely on them, cosmetic ordering by importance of api response objects for easier debugging for example... – dandavis May 08 '15 at 20:36
  • see: http://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties. As for what browser implement the ES6 property order, see: https://kangax.github.io/compat-table/es6/#own_property_order – slebetman May 08 '15 at 20:42
2

Continuing from the Rob's answer, you could get the items in sorted array of objects having the values like this:

var res = Object.keys(dict).map(function(key ) {
  return { key: key, value : dict[key] };
}).sort(function(a, b) { 
   return b.value - a.value
});

Where res is sorted array of form [{key:"alpha",value:16},...]

Which can then be converted to string using reduce

var strToFile = res.reduce( function(p,n) { 
 return p + n.value+" "+n.key+"\n"; },"");

The result looks something like

39 beta
12 alpha
Tero Tolonen
  • 4,144
  • 4
  • 27
  • 32
0

You could get the an array of the words that occur most frequently (though you won't have the values in the array);

Object.keys(dict).sort(function(a, b) { 
   return dict[b]-dict[a]; 
});
// ["beta", "alpha"]
Rob M.
  • 35,491
  • 6
  • 51
  • 50