var x = [{a:1, b:4,c:5}, {a:1, b:2,c:7}];
var y = [{a:1, b:2,c:6}, {a:1, b:2,c:8}];
I want to compare based on first 2 key i.e a,b and get the index if it is unequal. In above example output should be fetched as 0 since b value is nt equal. How do we achieve in javascript or Lodash ? Thank you.
So you want a compare function which will compare an array of object in which you want index of an object whose two properties a and b are not equal.
Below implementation return such index(0-base) if exit else return -1.
function compare(x,y){
for(let i=0;i<x.length && i<y.length;i++){
if(x[i].a!=y[i].a || x[i].b!=y[i].b)
return i;
}
return -1;
}
var x = [{a:1, b:4,c:5}, {a:1, b:2,c:7}];
var y = [{a:1, b:2,c:6}, {a:1, b:2,c:8}];
console.log(compare(x,y)); //0
y = [{a:1, b:4,c:6}, {a:1, b:2,c:8}];
console.log(compare(x,y));//-1
y = [{a:1, b:4,c:6}, {a:1, b:3,c:8}];
console.log(compare(x,y));//1
Hope this you want.
Assuming you want to compare two objects in an array by first two keys, here's a working example. Please, feel free to elaborate.
var x = [{a:1, b:4,c:5},{a:1, b:2,c:7}];
var y = [{a:1, b:2,c:6}, {a:1, b:2,c:8}];
function customizer(obj1, obj2) {
const [a, b] = Object.keys(obj1).slice(0, 2);
return obj1[a] === obj2[a] && obj1[b] === obj2[b];
}
xIsEqual = _.isEqualWith(x[0], x[1], customizer);
yIsEqual = _.isEqualWith(y[0], y[1], customizer);
console.log(xIsEqual); // false
console.log(yIsEqual); // true
If you want to filter an array on a specific condition, you can use filter() :
y.filter(d => d.a !== d.b);
With Lodash:
_.filter(y, d => d.a !== d.b);
You can't rely on an order of object keys, 'cause iteration over object keys is implementation-dependent. It could differ from browser to browser.
Read this note.
For example, run the following code:
const obj1 = { a: 'a', 100: 'b', 2: 'c' };
console.log('object literal', Object.keys(obj1));
// at least in chrome it would be ["2", "100", "a"]
const obj2 = {};
obj2['a'] = 'a';
obj2['100'] = 'b';
obj2['2'] = 'b';
console.log('object with keys added in a specific order', Object.keys(obj2));
// same ["2", "100", "a"]
Related
I'm trying to push an Object to an Array using the following command:
a = a.concat(b);
a = [];
b = {a: Object1, b: Object2 ....};
So I wan to have array a to be like a = [Object1, Object2...]
But the above statement results in:
a = [[Object1, Object2...][Object11, Object12...]];
How can I achieve this in JS?
If I´m understanding what you want, you want just objects, not the keys. So, could be:
for( var key in b ){
var value = b[key];
a.push(value);
}
console.log(JSON.stringify(a));
Hope it helps...
You are concatenating two arrays with Array.concat() you need to use Array.push().
var a = [ObjectA];
var b = ObjectB;
a.push(b);
// a = [ObjectA, ObjectB];
let a = [];
let b = {a:1, b:2, c:3};
for(obj in b){
let val = {};
val[obj] = b[obj];
a.push(val);
}
console.log(a);
Did you mean something like this ?
The Object.values() method returns an array of a given object's own enumerable property values,
You could use the Object values method Object.values(), like :
Object.values(b);
Example :
var a = [];
var obj_1 = {'attr_1': 'val_1', 'attr_2': 'val_2'};
var obj_2 = {'attr_3': 'val_3', 'attr_4': 'val_4'};
var b = {'a': obj_1, 'b': obj_2};
a = Object.values(b);
console.log( a );
Hope this helps.
Here is what I have, I've tried to do it in a for in loop and it's not quite doing what I need it to do. I thought of doing $.each, but I wasn't sure how to get past just looping the array.
var myKeys = [{a:1, b:10}, {a:5, b:8}, {a:3, b:2}, {a:1, b:6}];
I want to run a function that searches thru the keys. Finds the lowest 'b' value, and then returns the 'a' key. In the case above: search and find 2, but return 3 ( {a:3, b:2} )
Tried this, but I just get returned 0, 1, 2, 3 (all the keys) and their index.
for (var key in myKeys) {
if (myKeys.hasOwnProperty(myKeys[key]) <= 2) {
console.log(key);
}
}
Any insight on how to fix it would be great. If it were just the 1 value, I wouldn't have a problem. It's that there are multiple and I need to return the other value.
var myKeys = [{a:1, b:10}, {a:5, b:8}, {a:3, b:2}, {a:1, b:6}];
var min = myKeys.reduce(function(current, previous){
return (current.b > -1 && current.b < previous.b ? current : previous);
}, {a:-1, b:-1});
console.log(min);
Somehting like this?
var myKeys = [{a:1, b:10}, {a:5, b:8}, {a:3, b:2}, {a:1, b:6}];
for (var x = 0; x < myKeys.length; x++) {
var this_key = myKeys[x];
for (var key in this_key) {
if (this_key[key] <= 2) {
console.log(key);
}
}
};
I want to run a function that searches thru the keys. Finds the lowest
'b' value, and then returns the 'a' key. In the case above: search and
find 2, but return 3
You can create variables index, n to store index of element having "b" property of object and value of "b" property. Use for..of loop to iterate Array.prototype.entries() of array. Check if current object of array "b" property is less than previous object "b" property. Use index following for..of loop to get "a" property of myKeys[index].
var myKeys = [{a:1, b:10}, {a:5, b:8}, {a:3, b:2}, {a:1, b:6}];
let n = index = 0;
for (let [key, prop, curr] of myKeys.entries()) {
if (({b:curr} = prop, curr) < n || !n) [n, index] = [curr, key];
}
let {a} = myKeys[index]; // call `delete n` here if `n`:`2` not needed;
console.log({a});
I've 2 array of objects that I'd deeply compare with lodash
However, I've a prob with it:
> var x = [{a:1, b:2}, {c:3, d:4}];
> var y = [{b:2, a:1}, {d:4, c:3}];
> _.difference(x,y, _.isEqual);
[ { a: 1, b: 2 }, { c: 3, d: 4 } ]
How should I compare to see that both are equal?
You can make use of differenceWith() with an isEqual() comparator, and invoke isEmpty to check if they are equal or not.
var isArrayEqual = function(x, y) {
return _(x).differenceWith(y, _.isEqual).isEmpty();
};
var result1 = isArrayEqual(
[{a:1, b:2}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result2 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
document.write([
'<div><label>result1: ', result1, '</label></div>',
'<div><label>result2: ', result2, '</label></div>',
].join(''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>
UPDATE June 22, 2018
This update is in response to the comment below:
#ryeballar if any of the array is undefined it returns true. How would
you solve this. Thanks in advance buddy
As stated in the differenceWith documentation:
The order and references of result values are determined by the first
array.
This means that as long as all the items in the first array will match everything else in the second array, then the resulting array from the differenceWith invocation will be empty.
An alternative solution that truly solves the problem is to use xorWith() with the same chain of functions from the solution above.
var isArrayEqual = function(x, y) {
return _(x).xorWith(y, _.isEqual).isEmpty();
};
var result1 = isArrayEqual(
[{a:1, b:2}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result2 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result3 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}, undefined]
);
console.log('result1:', result1);
console.log('result2:', result2);
console.log('result3:', result3);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Following #ryeballar answer, if you only want to import specific lodash methods you can use this notation:
import { isEmpty, isEqual, xorWith } from 'lodash';
export const isArrayEqual = (x, y) => isEmpty(xorWith(x, y, isEqual));
Both answers on above with xorWith & differenceWith do not take in count if the array has different length only if the current item is found in the second array.
var isArrayEqual = function(x, y) {
return _(x).xorWith(y, _.isEqual).isEmpty();
};
var result = isArrayEqual(
[{a:1, b:2}],
[{a:1, b:2}, {a:1, b:2}]
);
console.log('result should be false:', result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
In that particular case, we also would have to compare both arrays length.
const isArrayEqual = function(x, y) {
const isSameSize = _.size(x) === _.size(y);
return isSameSize && _(x).xorWith(y, _.isEqual).isEmpty();
};
const result = isArrayEqual(
[{a:1, b:2}],
[{a:1, b:2}, {a:1, b:2}]
);
console.log('result should be false:', result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
I prefer pure JS since i haven't got the patience to learn underscore or lodash. So i invent something i have been long dreaming of. The Object.prototype.compare(). The v0.0.2 is doing only shallow comparison though but adequate for this question.
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
var obj1 = {a:1,b:2,c:3},
obj2 = {c:3,a:1,b:2},
obj3 = {b:2,c:3,a:7};
document.write ("<pre>" + obj1.compare(obj2) + "</pre>\n");
document.write ("<pre>" + obj2.compare(obj3) + "</pre>\n");
document.write ("<pre>" + new Object({a:1, b:2, c:3}).compare({c:3,b:2,a:1,d:0}) + "</pre>\n");
Cool... So then lets continue with the question... I guess... since we already have an Object.prototype.compare() there should be absolutely no harm in the invention of Array.prototype.compare(). Lets make it more clever this time. It shall tell primitives from objects. One other thing is, arrays are ordered; so in my book [1,2] is not equal to [2,1]. Also this makes the job simpler.
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
Array.prototype.compare = function(a){
return this.every((e,i) => typeof a[i] === "object" ? a[i].compare(e) : a[i] === e);
}
var x = [{a:1, b:2}, {c:3, d:4}],
y = [{b:2, a:1}, {d:4, c:3}],
a = [1,2,3,4,5],
b = [1,2,3,4,5],
p = "fourtytwo",
r = "thirtyseven",
n = 42,
m = 37;
document.writeln(x.compare(y)); // the question is answered here
document.writeln(a.compare(b));
document.writeln(p.compare(r)); // these primitives end up at Object prototype
document.writeln(n.compare(m)); // so modify Object.prototype.compare () accordingly
I've created an array in JavaScript and inserted objects with keys of object_ids:
var ar = [];
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
Problem is when I print this out the array I get is:
[undefined, undefined, "b", undefined, "a", "d", undefined, undefined, "c"]
and
ar.length = 9
How do I prevent the array from auto filling with undefined values and simply save this array as a 4-element array?
Iteration over an array is not what I expect here.
Thanks!
You can use an object literal instead of an array
var ar = {};
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
// {"2":"b","4":"a","5":"d","8":"c"}
You can iterate like this:
for (var i in a) {
console.log(a[i]);
}
Here's what you are doing:
var ar = [];
ar[8] = 'c'; // creates [undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'c'];
I believe this is what you want:
var ar = {};
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
Object.keys(ar).length == 4; // true
More information on Object.keys
I think you are confusing the behavior of JavaScript Arrays with the associative-array behavior of all JavaScript objects. Try this instead:
var a = {};
a[4] = 'a';
a[2] = 'b';
a[8] = 'c';
a[5] = 'd';
a; // {'2':'b', '4':'a', '8':'c', '5':'d'}
Note that the key/value pairs in an object are not ordered in any way. To order the keys or values you must maintain your own array of ordering.
what you want to do is probably:
array = {}
array["1"] = "b"
array["7"] = "aaa"
now array is:
Object { 1="a", 7="b"}
is it right?
var arr = {
0 : "hello",
8 : "world",
14: "!"
};
var count = 0;
for (var k in arr) {
++count;
}
document.write(arr[0] + " " + arr[8] + arr[14] + " with a length of " + count );
Outputs hello world! with a length of 3
You can use an Object.. and here is how to collect the count (if you needed)
Fiddle
There is no guarantee that iterating an Object using the for...in statement be in any specific order. The behavior is undefined in ECMA Script specification, quote:
The mechanics of enumerating the properties ... is implementation dependent.
Chrome doesn't iterate elements in order in many cases, last time I checked Opera went the same way and just now I read here that IE9 has adopted the same behavior. So your only solution that is guaranteed to keep both the association and order of elements is to use an Object to store keys and values and an Array to store the order:
var obj = { 4: 'a', 2: 'b', 8: 'c', 5: 'd' };
var order = [ 4, 2, 8, 5 ];
for( var i in order ) {
//do something with obj[ order[i] ]
}
I have the associative array:
array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;
What is the most elegant way to sort (descending) by its values where the result would be an array with the respective indices in this order:
sub2, sub3, sub1, sub4, sub0
Javascript doesn't have "associative arrays" the way you're thinking of them. Instead, you simply have the ability to set object properties using array-like syntax (as in your example), plus the ability to iterate over an object's properties.
The upshot of this is that there is no guarantee as to the order in which you iterate over the properties, so there is nothing like a sort for them. Instead, you'll need to convert your object properties into a "true" array (which does guarantee order). Here's a code snippet for converting an object into an array of two-tuples (two-element arrays), sorting it as you describe, then iterating over it:
var tuples = [];
for (var key in obj) tuples.push([key, obj[key]]);
tuples.sort(function(a, b) {
a = a[1];
b = b[1];
return a < b ? -1 : (a > b ? 1 : 0);
});
for (var i = 0; i < tuples.length; i++) {
var key = tuples[i][0];
var value = tuples[i][1];
// do something with key and value
}
You may find it more natural to wrap this in a function which takes a callback:
function bySortedValue(obj, callback, context) {
var tuples = [];
for (var key in obj) tuples.push([key, obj[key]]);
tuples.sort(function(a, b) {
return a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0
});
var length = tuples.length;
while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
}
bySortedValue({
foo: 1,
bar: 7,
baz: 3
}, function(key, value) {
document.getElementById('res').innerHTML += `${key}: ${value}<br>`
});
<p id='res'>Result:<br/><br/><p>
Instead of correcting you on the semantics of an 'associative array', I think this is what you want:
function getSortedKeys(obj) {
var keys = Object.keys(obj);
return keys.sort(function(a,b){return obj[b]-obj[a]});
}
for really old browsers, use this instead:
function getSortedKeys(obj) {
var keys = []; for(var key in obj) keys.push(key);
return keys.sort(function(a,b){return obj[b]-obj[a]});
}
You dump in an object (like yours) and get an array of the keys - eh properties - back, sorted descending by the (numerical) value of the, eh, values of the, eh, object.
This only works if your values are numerical. Tweek the little function(a,b) in there to change the sorting mechanism to work ascending, or work for string values (for example). Left as an exercise for the reader.
Continued discussion & other solutions covered at How to sort an (associative) array by value? with the best solution (for my case) being by saml (quoted below).
Arrays can only have numeric indexes. You'd need to rewrite this as either an Object, or an Array of Objects.
var status = new Array();
status.push({name: 'BOB', val: 10});
status.push({name: 'TOM', val: 3});
status.push({name: 'ROB', val: 22});
status.push({name: 'JON', val: 7});
If you like the status.push method, you can sort it with:
status.sort(function(a,b) {
return a.val - b.val;
});
There really isn't any such thing as an "associative array" in JavaScript. What you've got there is just a plain old object. They work kind-of like associative arrays, of course, and the keys are available but there's no semantics around the order of keys.
You could turn your object into an array of objects (key/value pairs) and sort that:
function sortObj(object, sortFunc) {
var rv = [];
for (var k in object) {
if (object.hasOwnProperty(k)) rv.push({key: k, value: object[k]});
}
rv.sort(function(o1, o2) {
return sortFunc(o1.key, o2.key);
});
return rv;
}
Then you'd call that with a comparator function.
The best approach for the specific case here, in my opinion, is the one commonpike suggested. A little improvement I'd suggest that works in modern browsers is:
// aao is the "associative array" you need to "sort"
Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});
This could apply easily and work great in the specific case here so you can do:
let aoo={};
aao["sub2"]=1;
aao["sub0"]=-1;
aao["sub1"]=0;
aao["sub3"]=1;
aao["sub4"]=0;
let sk=Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});
// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
// do something with sk[i] or aoo[sk[i]]
}
Besides of this, I provide here a more "generic" function you can use to sort even in wider range of situations and that mixes the improvement I just suggested with the approaches of the answers by Ben Blank (sorting also string values) and PopeJohnPaulII (sorting by specific object field/property) and lets you decide if you want an ascendant or descendant order, here it is:
// aao := is the "associative array" you need to "sort"
// comp := is the "field" you want to compare or "" if you have no "fields" and simply need to compare values
// intVal := must be false if you need comparing non-integer values
// desc := set to true will sort keys in descendant order (default sort order is ascendant)
function sortedKeys(aao,comp="",intVal=false,desc=false){
let keys=Object.keys(aao);
if (comp!="") {
if (intVal) {
if (desc) return keys.sort(function(a,b){return aao[b][comp]-aao[a][comp]});
else return keys.sort(function(a,b){return aao[a][comp]-aao[a][comp]});
} else {
if (desc) return keys.sort(function(a,b){return aao[b][comp]<aao[a][comp]?1:aao[b][comp]>aao[a][comp]?-1:0});
else return keys.sort(function(a,b){return aao[a][comp]<aao[b][comp]?1:aao[a][comp]>aao[b][comp]?-1:0});
}
} else {
if (intVal) {
if (desc) return keys.sort(function(a,b){return aao[b]-aao[a]});
else return keys.sort(function(a,b){return aao[a]-aao[b]});
} else {
if (desc) return keys.sort(function(a,b){return aao[b]<aao[a]?1:aao[b]>aao[a]?-1:0});
else return keys.sort(function(a,b){return aao[a]<aao[b]?1:aao[a]>aao[b]?-1:0});
}
}
}
You can test the functionalities trying something like the following code:
let items={};
items['Edward']=21;
items['Sharpe']=37;
items['And']=45;
items['The']=-12;
items['Magnetic']=13;
items['Zeros']=37;
//equivalent to:
//let items={"Edward": 21, "Sharpe": 37, "And": 45, "The": -12, ...};
console.log("1: "+sortedKeys(items));
console.log("2: "+sortedKeys(items,"",false,true));
console.log("3: "+sortedKeys(items,"",true,false));
console.log("4: "+sortedKeys(items,"",true,true));
/* OUTPUT
1: And,Sharpe,Zeros,Edward,Magnetic,The
2: The,Magnetic,Edward,Sharpe,Zeros,And
3: The,Magnetic,Edward,Sharpe,Zeros,And
4: And,Sharpe,Zeros,Edward,Magnetic,The
*/
items={};
items['k1']={name:'Edward',value:21};
items['k2']={name:'Sharpe',value:37};
items['k3']={name:'And',value:45};
items['k4']={name:'The',value:-12};
items['k5']={name:'Magnetic',value:13};
items['k6']={name:'Zeros',value:37};
console.log("1: "+sortedKeys(items,"name"));
console.log("2: "+sortedKeys(items,"name",false,true));
/* OUTPUT
1: k6,k4,k2,k5,k1,k3
2: k3,k1,k5,k2,k4,k6
*/
As I already said, you can loop over sorted keys if you need doing stuffs
let sk=sortedKeys(aoo);
// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
// do something with sk[i] or aoo[sk[i]]
}
Last, but not least, some useful references to Object.keys and Array.sort
Here is a variation of ben blank's answer, if you don't like tuples.
This saves you a few characters.
var keys = [];
for (var key in sortme) {
keys.push(key);
}
keys.sort(function(k0, k1) {
var a = sortme[k0];
var b = sortme[k1];
return a < b ? -1 : (a > b ? 1 : 0);
});
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = sortme[key];
// Do something with key and value.
}
No unnecessary complication required...
function sortMapByValue(map)
{
var tupleArray = [];
for (var key in map) tupleArray.push([key, map[key]]);
tupleArray.sort(function (a, b) { return a[1] - b[1] });
return tupleArray;
}
i use $.each of jquery but you can make it with a for loop, an improvement is this:
//.ArraySort(array)
/* Sort an array
*/
ArraySort = function(array, sortFunc){
var tmp = [];
var aSorted=[];
var oSorted={};
for (var k in array) {
if (array.hasOwnProperty(k))
tmp.push({key: k, value: array[k]});
}
tmp.sort(function(o1, o2) {
return sortFunc(o1.value, o2.value);
});
if(Object.prototype.toString.call(array) === '[object Array]'){
$.each(tmp, function(index, value){
aSorted.push(value.value);
});
return aSorted;
}
if(Object.prototype.toString.call(array) === '[object Object]'){
$.each(tmp, function(index, value){
oSorted[value.key]=value.value;
});
return oSorted;
}
};
So now you can do
console.log("ArraySort");
var arr1 = [4,3,6,1,2,8,5,9,9];
var arr2 = {'a':4, 'b':3, 'c':6, 'd':1, 'e':2, 'f':8, 'g':5, 'h':9};
var arr3 = {a: 'green', b: 'brown', c: 'blue', d: 'red'};
var result1 = ArraySort(arr1, function(a,b){return a-b});
var result2 = ArraySort(arr2, function(a,b){return a-b});
var result3 = ArraySort(arr3, function(a,b){return a>b});
console.log(result1);
console.log(result2);
console.log(result3);
Just so it's out there and someone is looking for tuple based sorts.
This will compare the first element of the object in array, than the second element and so on. i.e in the example below, it will compare first by "a", then by "b" and so on.
let arr = [
{a:1, b:2, c:3},
{a:3, b:5, c:1},
{a:2, b:3, c:9},
{a:2, b:5, c:9},
{a:2, b:3, c:10}
]
function getSortedScore(obj) {
var keys = [];
for(var key in obj[0]) keys.push(key);
return obj.sort(function(a,b){
for (var i in keys) {
let k = keys[i];
if (a[k]-b[k] > 0) return -1;
else if (a[k]-b[k] < 0) return 1;
else continue;
};
});
}
console.log(getSortedScore(arr))
OUPUTS
[ { a: 3, b: 5, c: 1 },
{ a: 2, b: 5, c: 9 },
{ a: 2, b: 3, c: 10 },
{ a: 2, b: 3, c: 9 },
{ a: 1, b: 2, c: 3 } ]
A modern approuch to this:
Object.fromEntries(Object.entries(data).sort((a,b)=>b[1]-a[1]).slice(0,5))
P.S: I did an optional slice, you can remove it if you want.
#commonpike's answer is "the right one", but as he goes on to comment...
most browsers nowadays just support Object.keys()
Yeah.. Object.keys() is WAY better.
But what's even better? Duh, it's it in coffeescript!
sortedKeys = (x) -> Object.keys(x).sort (a,b) -> x[a] - x[b]
sortedKeys
'a' : 1
'b' : 3
'c' : 4
'd' : -1
[ 'd', 'a', 'b', 'c' ]