I've been looking for a while and want a way to sort a Javascript object like this:
{
method: 'artist.getInfo',
artist: 'Green Day',
format: 'json',
api_key: 'fa3af76b9396d0091c9c41ebe3c63716'
}
and sort is alphabetically by name to get:
{
api_key: 'fa3af76b9396d0091c9c41ebe3c63716',
artist: 'Green Day',
format: 'json',
method: 'artist.getInfo'
}
I can't find any code that will do this. Can anyone give me some help?
UPDATE from the comments:
This answer is outdated. In ES6 objects keys are now ordered. See this question for an up-to-date answer
By definition, the order of keys in an object is undefined, so you probably won't be able to do that in a way that is future-proof. Instead, you should think about sorting these keys when the object is actually being displayed to the user. Whatever sort order it uses internally doesn't really matter anyway.
By convention, most browsers will retain the order of keys in an object in the order that they were added. So, you could do this, but don't expect it to always work:
function sortObject(o) {
var sorted = {},
key, a = [];
for (key in o) {
if (o.hasOwnProperty(key)) {
a.push(key);
}
}
a.sort();
for (key = 0; key < a.length; key++) {
sorted[a[key]] = o[a[key]];
}
return sorted;
}
this function takes an object and returns a sorted array of arrays of the form [key,value]
function (o) {
var a = [],i;
for(i in o){
if(o.hasOwnProperty(i)){
a.push([i,o[i]]);
}
}
a.sort(function(a,b){ return a[0]>b[0]?1:-1; })
return a;
}
The object data structure does not have a well defined order. In mathematical terms, the collection of keys in an object are an Unordered Set, and should be treated as such.
If you want to define order, you SHOULD use an array, because an array having an order is an assumption you can rely on. An object having some kind of order is something that is left to the whims of the implementation.
Just use sorted stringify() when you need to compare or hash the results.
// if ya need old browser support
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
var o = {c: 3, a: 1, b: 2};
var n = sortem(o);
function sortem(old){
var newo = {}; Object.keys(old).sort().forEach(function(k) {new[k]=old[k]});
return newo;
}
// deep
function sortem(old){
var newo = {}; Object.keys(old).sort().forEach(function(k){ newo[k]=sortem(old[k]) });
return newo;
}
sortem({b:{b:1,a:2},a:{b:1,a:2}})
Here is a one-liner for you.
Array.prototype.reduce()
let data = {
method: 'artist.getInfo',
artist: 'Green Day',
format: 'json',
api_key: 'fa3af76b9396d0091c9c41ebe3c63716'
};
let sorted = Object.keys(data).sort().reduce( (acc, currValue) => {
acc[currValue] = data[currValue];
return acc;
}, {});
console.log(sorted);
Good luck!!
ES5 Compatible:
function sortByKey(obj) {
var keys = Object.keys(obj);
keys.sort();
var sorted = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
sorted[key] = obj[key];
}
return sorted;
}
This should be used with caution as your code shouldn't rely on Object properties order. If it's just a matter of presentation (or just for the fun !), you can sort properties deeply like this :
function sortObject(src) {
var out;
if (typeof src === 'object' && Object.keys(src).length > 0) {
out = {};
Object.keys(src).sort().forEach(function (key) {
out[key] = sortObject(src[key]);
});
return out;
}
return src;
}
Related
I am looking for a short and efficient way to filter objects by key, I have this kind of data-structure:
{"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]}
Now I want to filter by keys, for example by "Key1":
{"Key1":[obj1,obj2,obj3]}
var object = {"Key1":[1,2,3], "Key2":[4,5,6]};
var key1 = object["Key1"];
console.log(key1);
you can use the .filter js function for filter values inside an object
var keys = {"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]};
var objectToFind;
var keyToSearch = keys.filter(function(objects) {
return objects === objectToFind
});
The keyToSearch is an array with all the objects filter by the objectToFind variable.
Remember, in the line return objects === objectToFind is where you have to should your statement. I hope it can help you.
You can create a new object based on some custom filter criteria by using a combination of Object.keys and the array .reduce method. Note this only works in es6:
var myObject = {"Key1":["a","b","c"], "Key2":["e","f","g"]}
function filterObjectByKey(obj, filterFunc) {
return Object.keys(obj).reduce((newObj, key) => {
if (filterFunc(key)) {
newObj[key] = obj[key];
}
return newObj;
}, {});
}
const filteredObj = filterObjectByKey(myObject, x => x === "Key1")
console.log(filteredObj)
Not sure what exactly are you trying to achieve, but if you want to have a set of keys that you would like to get the data for, you have quite a few options, one is:
var keys = ['alpha', 'bravo'];
var objectToFilterOn = {
alpha: 'a',
bravo: 'b',
charlie: 'c'
};
keys.forEach(function(key) {
console.log(objectToFilterOn[key]);
});
I want to reverse the mapping of an object (which might have duplicate values). Example:
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France'
};
reverseMapping(city2country) Should output:
{
'Netherlands': ['Amsterdam', 'Rotterdam'],
'France': ['Paris']
}
I've come up with the following, naive solution:
const reverseMapping = (obj) => {
const reversed = {};
Object.keys(obj).forEach((key) => {
reversed[obj[key]] = reversed[obj[key]] || [];
reversed[obj[key]].push(key);
});
return reversed;
};
But I'm pretty sure there is a neater, shorter way, preferably prototyped so I could simply do:
const country2cities = city2country.reverse();
You could use Object.assign, while respecting the given array of the inserted values.
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
const reverseMapping = o => Object.keys(o).reduce((r, k) =>
Object.assign(r, { [o[k]]: (r[o[k]] || []).concat(k) }), {})
console.log(reverseMapping(city2country));
There is no such built-in function in JavaScript. Your code looks fine, but given that there are so many edge cases here that could wrong, I'd suggesting using invertBy from lodash, which does exactly what you describe.
Example
var object = { 'a': 1, 'b': 2, 'c': 1 };
_.invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }
You can use something like this to get raid of duplicates first :
function removeDuplicates(arr, key) {
if (!(arr instanceof Array) || key && typeof key !== 'string') {
return false;
}
if (key && typeof key === 'string') {
return arr.filter((obj, index, arr) => {
return arr.map(mapObj => mapObj[key]).indexOf(obj[key]) === index;
});
} else {
return arr.filter(function(item, index, arr) {
return arr.indexOf(item) == index;
});
}
}
and then use this to make it reverse :
function reverseMapping(obj){
var ret = {};
for(var key in obj){
ret[obj[key]] = key;
}
return ret;
}
You could try getting an array of values and an array of keys from the current object, and setup a new object to hold the result. Then, as you loop through the array of values -
if the object already has this value as the key, like Netherlands, you create a new array, fetch the already existing value (ex: Rotterdam), and add this and the new value (Amsterdam) to the array, and set up this array as the new value for the Netherlands key.
if the current value doesn't exist in the object, set it up as a new string, ex: France is the key and Paris is the value.
Code -
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France',
};
function reverseMapping(obj) {
let values = Object.values(obj);
let keys = Object.keys(obj);
let result = {}
values.forEach((value, index) => {
if(!result.hasOwnProperty(value)) {
// create new entry
result[value] = keys[index];
}
else {
// duplicate property, create array
let temp = [];
// get first value
temp.push(result[value]);
// add second value
temp.push(keys[index]);
// set value
result[value] = temp;
}
});
console.log(result);
return result;
}
reverseMapping(city2country)
The benefit here is - it adjusts to the structure of your current object - Netherlands being the repeated values, gets an array as it's value in the new object, while France gets a string value Paris as it's property. Of course, it should be very easy to change this.
Note - Object.values() might not be supported across older browsers.
You could use reduce to save the declaration line reduce.
Abusing && to check if the map[object[key]] is defined first before using Array.concat.
It's shorter, but is it simpler? Probably not, but a bit of fun ;)
const reverseMapping = (object) =>
Object.keys(object).reduce((map, key) => {
map[object[key]] = map[object[key]] && map[object[key]].concat(key) || [key]
return map;
}, {});
#Nina Scholz answer works well for this exact question. :thumbsup:
But if you don't need to keep both values for the Netherlands key ("Netherlands": ["Amsterdam", "Rotterdam"]), then this is a little bit shorter and simpler to read:
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
console.log(
Object.entries(city2country).reduce((obj, item) => (obj[item[1]] = item[0]) && obj, {})
);
// outputs `{Netherlands: "Rotterdam", France: "Paris"}`
Lets say there are two objects but one object has property different from the other. Is there a way to figure out what properties match?
for example:
var objectOne = {
boy: "jack",
girl: "jill"
}
var objectTwo = {
boy: "john",
girl: "mary",
dog: "mo"
}
edit: It should tell me boy and girl property name are found in both the objects.
var in_both = [];
for (var key in objectOne) { // simply iterate over the keys in the first object
if (Object.hasOwnProperty.call(objectTwo, key)) { // and check if the key is in the other object, too
in_both.push(key);
}
}
C.f. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
Now, if you want to test if the values are the same, too, than simply add more code to the condition/body of the inner if.
Using Object.keys
Object.keys(objectOne).filter(k => Object.hasOwnProperty.call(objectTwo, k))
You can use Object.keys and use Array.prototype.reduce to loop through once and list out the common keys - see demo below:
var objectOne={boy:"jack",girl:"jill"};
var objectTwo={boy:"john",girl:"mary",dog:"mo"};
var result = Object.keys(objectOne).reduce(function(p,c){
if(c in objectTwo)
p.push(c);
return p;
},[]);
console.log(result);
If you want to find out which keys match given two objects, you could loop through all of the keys of the objects using a for... in loop. In my function, it will loop through the keys and return an array of all of the matching keys in the two objects.
let objectOne = {
boy: "jack",
girl: "jill"
}
let objectTwo = {
boy: "john",
girl: "mary",
dog: "mo"
}
function matchingKeys (obj1, obj2) {
let matches = [];
let key1, key2;
for (key1 in obj1) {
for (key2 in obj2) {
if ( key1 === key2) {
matches.push(key1);
}
}
}
return matches
}
const result = matchingKeys(objectOne, objectTwo);
console.log(result)
Try this on for size:
function compare(obj1, obj2) {
// get the list of keys for the first object
var keys = Object.keys(obj1);
var result = [];
// check all from the keys in the first object
// if it exists in the second object, add it to the result
for (var i = 0; i < keys.length; i++) {
if (keys[i] in obj2) {
result.push([keys[i]])
}
}
return result;
}
This isn't better than some solutions here, but I thought I'd share:
function objectHas(obj, predicate) {
return JSON.stringify(obj) === JSON.stringify({ ...obj, ...predicate })
}
I have an object like so:
> Object
> Rett#site.com: Array[100]
> pel4#gmail.com: Array[4]
> 0
id : 132
selected : true
> 1
id : 51
selected : false
etc..
How can I use the underscore _.filter() to return back only the items where selected === true?
I've never had the need to go down to layers with _.filter(). Something like
var stuff = _.filter(me.collections, function(item) {
return item[0].selected === true;
});
Thank you
If you want to pull all array elements from any e-mail address where selected is true, you can iterate like so:
var selected = [];
for (email in emailLists) {
selected.concat(_.filter(emailLists[email], function (item) {
return item.selected === true;
}));
}
If you only want to pull the arrays where all elements are selected, you might instead do something like this:
var stuff = _.filter(me.collections, function(item) {
return _.all(item, function (jtem) {
jtem.selected === true;
});
});
Underscore's filter method will work on an object being used as a hash or dictionary, but it will return an array of the object's enumerable values and strip out the keys. I needed a function to filter a hash by its values that would preserve the keys, and wrote this in Coffeescript:
hash_filter: (hash, test_function) ->
keys = Object.keys hash
filtered = {}
for key in keys
filtered[key] = hash[key] if test_function hash[key]
filtered
If you're not using Coffeescript, here's the compiled result in Javascript, cleaned up a little:
hash_filter = function(hash, test_function) {
var filtered, key, keys, i;
keys = Object.keys(hash);
filtered = {};
for (i = 0; i < keys.length; i++) {
key = keys[i];
if (test_function(hash[key])) {
filtered[key] = hash[key];
}
}
return filtered;
}
hash = {a: 1, b: 2, c: 3};
console.log((hash_filter(hash, function(item){return item > 1;})));
// Object {b=2, c=3}
TL; DR: Object.keys() is great!
I have an object called allFilterValues containing the following:
{"originDivision":"GFC","originSubdivision":"","destinationDivision":"","destinationSubdivision":""}
This is ugly but you asked for an underscore based way to filter an object. This is how I returned only the filter elements that had non-falsy values; you can switch the return statement of the filter to whatever you need:
var nonEmptyFilters = _.pick.apply({}, [allFilterValues].concat(_.filter(_.keys(allFilterValues), function(key) {
return allFilterValues[key];
})));
Output (JSON/stringified):
{"originDivision":"GFC"}
#Dexygen was right to utilize _.pick but a cleaner solution is possible because the function also accepts a predicate
Return a copy of the object, filtered to only have values for the allowed keys (or array of valid keys). Alternatively accepts a predicate indicating which keys to pick.
(highlight is mine)
Here's a real life example I've used in a project
_.pick({red: false, yellow: true, green: true}, function(value, key, object) {
return value === true;
});
// {yellow: true, green: true}
const obj = {
1 : { active: true },
2 : { active: false },
3 : { active: false },
}
let filtered = Object.entries(obj).reduce((acc, current) => {
const currentEntry = current[1];
const currentKey = current[0];
//here you check condition
if (currentEntry.active) {
return {
...acc,
[currentKey]: currentEntry
}
}
return acc;
}, {})
There is a rule of thumb, if you need to achieve something really exotic look up into reducer it can solve almost all problems related to objects, it's a bit tricky to get used to it, but trust me thorough reading of documentation gonna pay off.
Maybe you want a simplest way
_.filter(me.collections, { selected: true})
Consider:
var object = {
foo: {},
bar: {},
baz: {}
}
How would I do this:
var first = object[0];
console.log(first);
Obviously, that doesn’t work because the first index is named foo,
not 0.
console.log(object['foo']);
works, but I don’t know it’s named foo. It could be named anything. I just want the first.
Just for fun this works in JS 1.8.5
var obj = {a: 1, b: 2, c: 3};
Object.keys(obj)[0]; // "a"
This matches the same order that you would see doing
for (o in obj) { ... }
If you want something concise try:
for (first in obj) break;
alert(first);
wrapped as a function:
function first(obj) {
for (var a in obj) return a;
}
they're not really ordered, but you can do:
var first;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof(i) !== 'function') {
first = obj[i];
break;
}
}
the .hasOwnProperty() is important to ignore prototyped objects.
This will not give you the first one as javascript objects are unordered, however this is fine in some cases.
myObject[Object.keys(myObject)[0]]
If the order of the objects is significant, you should revise your JSON schema to store the objects in an array:
[
{"name":"foo", ...},
{"name":"bar", ...},
{"name":"baz", ...}
]
or maybe:
[
["foo", {}],
["bar", {}],
["baz", {}]
]
As Ben Alpert points out, properties of Javascript objects are unordered, and your code is broken if you expect them to enumerate in the same order that they are specified in the object literal—there is no "first" property.
for first key of object you can use
console.log(Object.keys(object)[0]);//print key's name
for value
console.log(object[Object.keys(object)[0]]);//print key's value
There is no way to get the first element, seeing as "hashes" (objects) in JavaScript have unordered properties. Your best bet is to store the keys in an array:
var keys = ["foo", "bar", "baz"];
Then use that to get the proper value:
object[keys[0]]
ES6
const [first] = Object.keys(obj)
Using underscore you can use _.pairs to get the first object entry as a key value pair as follows:
_.pairs(obj)[0]
Then the key would be available with a further [0] subscript, the value with [1]
I had the same problem yesterday. I solved it like this:
var obj = {
foo:{},
bar:{},
baz:{}
},
first = null,
key = null;
for (var key in obj) {
first = obj[key];
if(typeof(first) !== 'function') {
break;
}
}
// first is the first enumerated property, and key it's corresponding key.
Not the most elegant solution, and I am pretty sure that it may yield different results in different browsers (i.e. the specs says that enumeration is not required to enumerate the properties in the same order as they were defined). However, I only had a single property in my object so that was a non-issue. I just needed the first key.
You could do something like this:
var object = {
foo:{a:'first'},
bar:{},
baz:{}
}
function getAttributeByIndex(obj, index){
var i = 0;
for (var attr in obj){
if (index === i){
return obj[attr];
}
i++;
}
return null;
}
var first = getAttributeByIndex(object, 0); // returns the value of the
// first (0 index) attribute
// of the object ( {a:'first'} )
To get the first key of your object
const myObject = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
}
const result = Object.keys(myObject)[0];
// result will return 'foo1'
Based on CMS answer. I don't get the value directly, instead I take the key at its index and use this to get the value:
Object.keyAt = function(obj, index) {
var i = 0;
for (var key in obj) {
if ((index || 0) === i++) return key;
}
};
var obj = {
foo: '1st',
bar: '2nd',
baz: '3rd'
};
var key = Object.keyAt(obj, 1);
var val = obj[key];
console.log(key); // => 'bar'
console.log(val); // => '2nd'
My solution:
Object.prototype.__index = function(index)
{
var i = -1;
for (var key in this)
{
if (this.hasOwnProperty(key) && typeof(this[key])!=='function')
++i;
if (i >= index)
return this[key];
}
return null;
}
aObj = {'jack':3, 'peter':4, '5':'col', 'kk':function(){alert('hell');}, 'till':'ding'};
alert(aObj.__index(4));