In JavaScript I am trying to convert an array of objects with similar keys:
[{'a':1,'b':2}, {'a':3,'b':4}, {'a':5,'b':6,'c':7}]
to an object with an array of values for each key:
{'a':[1,3,5], 'b':[2,4,6], 'c':[7]};
using underscore.js 1.4.2.
I have some working code below, but it feels longer and clunkier than just writing nested for loops.
Is there a more elegant way of doing this in underscore? Is there something simple I'm missing?
console.clear();
var input = [{'a':1,'b':2},{'a':3,'b':4},{'a':5,'b':6,'c':7}];
var expected = {'a':[1,3,5], 'b':[2,4,6], 'c':[7]};
// Ok, go
var output = _(input)
.chain()
// Get all object keys
.reduce(function(memo, obj) {
return memo.concat(_.keys(obj));
}, [])
// Get distinct object keys
.uniq()
// Get object key, values
.map(function(key) {
// Combine key value variables to an object
// ([key],[[value,value]]) -> {key: [value,value]}
return _.object(key,[
_(input)
.chain()
// Get this key's values
.pluck(key)
// Filter out undefined
.compact()
.value()
]);
})
// Flatten array of objects to a single object
// [{key1: [value]}, {key2, [values]}] -> {key1: [values], key2: [values]}
.reduce(function(memo, obj) {
return _.extend(memo, obj);
}, {})
.value();
console.log(output);
console.log(expected);
console.log(_.isEqual(output, expected));
Thanks
Sounds like you want zip for objects. This would be the analogous method for objects:
_.transpose = function(array) {
var keys = _.union.apply(_, _.map(array, _.keys)),
result = {};
for (var i=0, l=keys.length; i<l; i++) {
var key = keys[i];
result[key] = _.pluck(array, key);
}
return result;
};
However, I would just use
_.transpose = function(array) {
var result = {};
for (var i=0, l=array.length; i<l)
for (var prop in array[i])
if (prop in result)
result[prop].push(array[i][prop]);
else
result[prop] = [ array[i][prop] ];
return result;
};
without any Underscore at all :-) Of course, you could use some iterator methods, it then might look like
_.reduce(array, function(map, obj) {
return _.reduce(obj, function(map, val, key) {
if (key in map)
map[key].push(val)
else
map[key] = [val];
return map;
}, map);
}, {});
You can use lodash's zipObject mehtod: https://lodash.com/docs#zipObject
You need 3 lines of lodash:
_.merge.apply(null, _.union([{}], myArrayOfObjects, [function (a, b) {
return _.compact(_.flatten([a, b]));
}]))
See the docs of _.merge for more details on what the function does.
Related
I just started learning to code and am working on this challenge. Having issues finding any relevant guidance online. Thanks in advance.
function keys(json) {
var obj = JSON.parse(json);
let result = [];
for (const key in obj) {
result.push(key);
}
return result;
}
I got this to work for returning keys without using Object.keys and I sort of assumed I could just swap the 'key' for 'value' but that didn't work.
function keys(json) {
var obj = JSON.parse(json);
var values = Object.keys(obj).map((key,value)=>{ values.push(value) });
return values;
}
Also tried this, but I don't really understand the map function yet.
Is this what you are looking for?
const obj = {a:3, b:4}
const result = []
for (const key in obj) {
result.push(obj[key])
}
console.log(result)
With Object.keys you get an array of keys (own, enummerable) of the array. for mapping values, you need to take a property accessor with object and key.
function values(json) {
const object = JSON.parse(json);
return Object
.keys(object)
.map(key => object[key]);
}
console.log(values('{"foo":"bar","baz":42}'));
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;
}
Lets say I receive a parsed json like below:
[{"a":1},{"a":2},{"a":3}]
The keys are the same which is a.
How do I make each a unique so that the map is usable?'
EDIT1:
Results I want:
let myMap = {}; //I declare my variable
//Then I fetch a json and parse it
fetch(link)
.then(function(response) {
return response.json(); //parse the json string
}).then(function(json) {
myMap = json; //set it to myMap to be used
}
For some reason I having duplicate keys although you guys said the json is unique. Do I have to set the json string to myMap first and then only parse it?
Basically you can use an Object as hash table
var data = [{ "a": 1 }, { "a": 2 }, { "a": 3 }],
object = Object.create(null);
data.forEach(function (el) {
object[el.a] = el;
});
console.log(object);
Or a Map
var data = [{ "a": 1 }, { "a": 2 }, { "a": 3 }],
map = new Map;
data.forEach(function (el) {
map.set(el.a, el);
});
console.log(map.get(1));
The advantage of Map over an Object is, the key can be anything. The key is not converted to string. Maps can have an object or other primitive or not primitive values as key.
Also if you have a single value list or want to make sure it IS unique you can use the index supplied like this:
obj.map((item, index) =>
...
)}
Maybe this?
[{a:1},{a:2},{a:3}].map(function(item, index) { item.id = index; return item; });
Map in javascript doesnot need a unique id, it will iterate through all the value. so it will iterate through all the objects irrespective the fact that the key is same
eg:
var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}]
var reformattedArray = kvArray.map(function(obj){
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
Well, [{a:1},{a:2},{a:3}] is already unique... but, It's an Array.
so you cannot access an object {a:2, ...} directly but find index with looping.
If I understand your question right way... you want to make new MAP with unique key a how about this way? - reduce can help us. :)
btw, Nina Scholz's answer is right.
let myMap = {}; //I declare my variable
//Then I fetch a json and parse it
fetch(link)
.then(function(response) {
return response.json(); //parse the json string
}).then(function(json) {
// myMap = json; //set it to myMap to be used
myMap = json.reduce(function(p, n) { p[n.a] = n; return p; }, {});
// n.a or n['a'] - primary key (in sample, [1,2,3...])
// "myMap[1].foo", "myMap[2].bar"
// or change KEY as your taste.
myMap = json.reduce(function(p, n) { p['k' + n.a] = n; return p; }, {});
// "myMap.k1.foo", "myMap.k2.bar"
// It's unique but if you want everything...
myMap = json.reduce(function(p, n) {
var k = 'k' + n.a;
if(p[k] !== undefined) { p[k] = n; }
else if(Array.isArray(p[k])) { p[k].push(n); }
else { p[k] = [p[k]] ; }
return p;
}, {});
}
This question already has answers here:
From an array of objects, extract value of a property as array
(24 answers)
Closed 8 years ago.
If I have an object such that
var object = function(key,text)
{
this.key = key;
this.text = text;
}
And create an array of these objects
var objArray = [];
objArray[0] = new object('key1','blank');
objArray[1] = new object('key2','exampletext');
objArray[2] = new object('key3','moretext');
is there a way that I can retrieve only one of the properties of all of the objects in the array? For example:
var keyArray = objArray["key"];
The above example doesn't return set keyArray to anything, but I was hoping it would be set to something like this:
keyArray = [
'key1',
'key2',
'key3']
Does anyone know of a way to do this without iterating through the objArray and manually copying each key property to the key array?
This is easily done with the Array.prototype.map() function:
var keyArray = objArray.map(function(item) { return item["key"]; });
If you are going to do this often, you could write a function that abstracts away the map:
function pluck(array, key) {
return array.map(function(item) { return item[key]; });
}
In fact, the Underscore library has a built-in function called pluck that does exactly that.
var object = function(key,text) {
this.key = key;
this.text = text;
}
var objArray = [];
objArray[0] = new object('key1','blank');
objArray[1] = new object('key2','exampletext');
objArray[2] = new object('key3','moretext');
var keys = objArray.map(function(o,i) {
return o.key;
});
console.log(keys); // ["key1", "key2", "key3"]
JS Bin Example
http://jsbin.com/vamey/1/edit
Note that older browsers may not support map but you can easily do this with a for loop:
var keys = [];
for (var i = 0; i < objArray.length; i++) {
keys.push(objArray[i].key);
}
JS Bin Example
http://jsbin.com/redis/1/edit
You would want to do something like this:
objArray.map(function (obj) { return obj.key; });
Here is a JSFiddle to demo: http://jsfiddle.net/Q7Cb3/
If you need older browser support, you can use your own method:
JSFiddle demo: http://jsfiddle.net/Q7Cb3/1/
function map (arr, func) {
var i = arr.length;
arr = arr.slice();
while (i--) arr[i] = func(arr[i]);
return arr;
}
Well something has to iterate through the elements of the array. You can use .map() to make it look nice:
var keys = objArray.map(function(o) { return o.key; });
You could make a function to generate a function to retrieve a particular key:
function plucker(prop) {
return function(o) {
return o[prop];
};
}
Then:
var keys = objArray.map(plucker("key"));
Really "objArray" is an array that have 3 objects inside, if you want list of keys, you can try this:
var keys = [];
for(a in objArray) {
keys.push(objArray[a].key);
}
You have in var keys, the three keys.
Hope that helps! :)
I used a literal as a dictionary, but a third party binding tool only takes arrays.
This is one way, is there a better one?
var arr = [];
$.each(objectLiteral, function () { arr.push(this); });
I think there is nothing wrong with your solution.
This is a shorter one:
var arr = $.map(objectLiteral, function (value) { return value; });
Your method is fine, clear and readable. To do it without jQuery, use the for (..in..) syntax:
var arr = [];
for (prop in objectLiteral) {
arr.push(objectLiteral[prop]);
}
In vanilla JS...
If we want to convert an object literal
var obj = {
species: 'canine',
name: 'Charlie',
age: 4
}
into an array of arrays
[['species', 'canine'], ['name', 'Charlie'], ['age', 4]]
here is one way
function objToArr(obj){
var arr = [];
for (var key in obj){
arr.push([key, obj[key]]);
}
return arr;
}
const objectLiteral = { hell: 'devil' };
const ver1 = Object.keys(objectLiteral); // ['hell']
const ver2 = Object.values(objectLiteral); // ['devil']
const ver3 = Object.entries(objectLiteral); // [['hell', 'devil']]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/keys
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/entries
In ES2017 you can now use Object.entries and with ES6 destructuring support you can use the resulting array pretty nice, example
Object.entries(objectLiteral).filter(([key, value]) => !!value).map(([key, value]) => key)
Gets you all properties with a value