I want to return the key of the first object returned by the _.orderBy() method which sorts by a nested property.
Here is the CodePen demo. In this case, the key I want to return is "charlie".
console.clear();
const ob = {
"alpha": {
"id": 27,
"lottery": {
"id": 1,
"name": "La Primitiva",
"jackpotAmount": 500,
}
},
"bravo": {
"id": 28,
"lottery": {
"id": 1,
"name": "La Primitiva",
"jackpotAmount": 10,
}
},
"charlie": {
"id": 29,
"lottery": {
"id": 1,
"name": "La Primitiva",
"jackpotAmount": 1000,
}
},
}
const out = _.orderBy(ob, (e) => {
return e.lottery.jackpotAmount;
}, ['desc'] ); // How do I get key of first property, "charlie"?
console.log(out);
This isn't the most efficient way of getting the highest jackpotAmount. Sorting is O(n log n ). You can find the highest value in O(n) time. This is also creating a temporary copy of the list so it uses a lot of extra memory for a temporary operation. Using _.maxBy() would be more efficient.
var x = _.maxBy(ob, (e) => { return e.lottery.jackpotAmount; });
return x.id;
These functions work on lists. To get the key rather than item, you need to iterate over the keys.
var keys = Object.keys(ob);
return _.maxBy(keys, (e) => { return ob[e].lottery.jackpotAmount; });
Note that this doesn't entirely solve the efficiency issues. Depending on the javascript engine, the overhead of needing the keys array and the ob[e] lookup degrade space usage to O(n) and potentially a complexity of O(n log n).
If you're targeting a modern JS engine, you can use the Map object with a hand-written search to get constant space and linear time search.
const getMax = function (map) {
let maxValue = null;
let maxKey = null;
map.forEach((value, key) => {
if (value.lottery.jackpotAmount > maxValue) {
maxValue = value.lottery.jackpotAmount;
maxKey = key;
}
});
return maxKey;
};
Here's a Codepen demo.
Cheers
Related
I have a array as follows:
data = [
{
"id":1
"name":"london"
},
{
"id":2
"name":"paris"
},
{
"id":3
"name":"london"
},
{
"id":4
"name":"paris"
},
{
"id":5
"name":"australia"
},
{
"id":6
"name":"newzearland"
}
]
At runtime this array can have n number of elements. I want to group this array with respect to name attribute. All the elements with same name should be moved to a separate array. I don't know the what value can name have in advance. This is coming at runtime. For example, from above array I want final output as follows:
output:
newArray1 = [
{
"id":1
"name":"london"
},
{
"id":3
"name":"london"
}
]
newArray2 = [
{
"id":2
"name":"paris"
},
{
"id":4
"name":"paris"
}
]
newArray3 = [
{
"id":5
"name":"australia"
}
]
newArray4 = [
{
"id":6
"name":"newzearland"
}
]
How can I do that?
As Teemu has already pointed out in a comment, creating new variables to store the data is not ideal. You would have no way of knowing how many groups you've created and using variables that you can't be sure exist is not the best way to write code. Fortunately, JavaScript has objects, which can store data like this in a much cleaner way. Here's the code I've come up with:
function groupBy(arr, key) {
let res = {}
for (let element of arr) {
if (res.hasOwnProperty(element[key])) {
res[element[key]].push(element)
} else {
res[element[key]] = [element]
}
}
return res
}
This code is not the best, most efficient code ever, but it is written to be easier to understand for someone still learning. This code loops over every element in your data and checks whether our result already contains an array for elements with that name. If there's already an array for elements with that name, the current element is added to it. If there isn't one, a new one is created with the current element inside it. To do exactly what you want, you'd call this function with groupBy(data, "name") and assign it to a new variable like groupedData (THIS DOES NOT MODIFY THE DATA, IT RETURNS A NEW OBJECT OF GROUPED DATA) .
Start by getting all the unique .names, then map them to the original array filtered by each .name:
const data = [{
"id": 1, "name": "london"
},
{
"id": 2, "name": "paris"
},
{
"id": 3, "name": "london"
},
{
"id": 4, "name": "paris"
},
{
"id": 5, "name": "australia"
},
{
"id": 6, "name": "newzearland"
}
];
const newData = [...new Set(data
//Get all names in an array
.map(({name}) => name))]
//For each name filter original array by name
.map(n => data.filter(({name}) => n === name));
console.log( newData );
//OUTPUT: [newArray1, newArray2, .....]
You can get the expected result with grouping by key approach.
const data = [{"id":1,"name":"london"},{"id":2,"name":"paris"},{"id":3,"name":"london"},{"id":4,"name":"paris"},{"id":5,"name":"australia"},{"id":6,"name":"newzearland"}];
const result = Object.values(data.reduce((acc, obj) =>
({ ...acc, [obj.name]: [...(acc[obj.name] ?? []), obj] }), {}));
console.log(result);
const [newArray1, newArray2, newArray3, newArray4, ...rest] = result;
console.log('newArray1:', newArray1);
console.log('newArray2:', newArray2);
console.log('newArray3:', newArray3);
console.log('newArray4:', newArray4);
.as-console-wrapper{min-height: 100%!important; top: 0}
Is there a built-in Javascript function to transform a list of dictionaries:
const L =
[ { "day": "20201210", "count": "100" }
, { "day": "20201211", "count": "120" }
, { "day": "20201212", "count": "90" }
, { "day": "20201213", "count": "150" }
]
into a dictionary of lists like this:
const D =
{ "day" : [ "20201210", "20201211", "20201212", "20201213"]
, "count" : [ "100", "120", "90", "150"]
}
? If not what's the simplest way to do it in JS?
(It is a little-bit similar to matrix "transpose" operation).
Note: here it's a transpose and not a groupby like in Most efficient method to groupby on an array of objects
Assuming all objects have the same keys and your array is not empty, this will work:
let D = {};
Object.keys(L[0]).forEach(k => {
D[k] = L.map(o => o[k]);
});
There are certainly more efficient solutions but this is short and concise and not too bad in terms of efficiency.
Here's a fairly short and efficient method for general values.
L.forEach(o => {
Object.keys(o).forEach(k => {
D[k] ||= [];
D[k].push(o[k]);
});
});
const L = [{
"day": "20201210",
"count": "100"
}, {
"day": "20201211",
"count": "120"
}, {
"day": "20201212",
"count": "90"
}, {
"day": "20201213",
"count": "150"
}]
let D = {};
L.forEach(o => {
Object.keys(o).forEach(k => {
D[k] ||= [];
D[k].push(o[k]);
});
});
console.log(D);
I don't think such a function exists, at least in vanilla JavaScript.
A simple, pure vanilla and clear to understand way of doing it would be like this:
var L = [
{
"day": "20201210",
"count": "100"
},
{
"day": "20201211",
"count": "120"
},
{
"day": "20201212",
"count": "90"
},
{
"day": "20201213",
"count": "150"
}
];
var D = { };
for (var dict in L)
{
for (var key in L[dict])
{
if (D[key] == null) {
D[key] = [ ];
}
D[key].push(L[dict][key]);
}
}
This is definitely not the most concise or most optimized way of doing it, though it will work.
You can just restructure your array of dictionaries as such, and re-map it with Array.prototype.map
E.g. (The following practice requires to iterate the elements with map N * X times where N is the length of L and X is the amount of properties you'd want to have in D, ignore this if you have many properties you'd want to watch.)
But, this is the easiest readable approach that I'd want to introduce to you before the 2nd approach.
const L = [{"day":"20201210","count":"100"},{"day":"20201211","count":"120"},{"day":"20201212","count":"90"},{"day":"20201213","count":"150"}];
const D = {
'day': L.map(elem => elem['day']),
'count': L.map(elem => elem['count']),
};
console.log(D);
Another approach I'd suggest is to use Array.prototype.reduce, this is by far favored in your case as it's easily expandable by adding more properties to the initial array.
const L = [{"day":"20201210","count":"100"},{"day":"20201211","count":"120"},{"day":"20201212","count":"90"},{"day":"20201213","count":"150"}];
const D = L.reduce((acc, cv) => {
for (const propertyToGrab in acc) {
if (cv.hasOwnProperty(propertyToGrab)) {
acc[propertyToGrab].push(cv[propertyToGrab]);
}
}
return acc;
}, {
'day': [],
'count': []
});
console.log(D);
const D={day:[], count:[]};
for(const item of L){
D.day.push(item.day);
D.count.push(item.count);
}
const input = [{"day":"20201210","count":"100"},{"day":"20201211","count":"120"},{"day":"20201212","count":"90"},{"day":"20201213","count":"150"}];
// Create a variable that will store the result
let result = {};
// Loop through the input with forEach
input.forEach((element) => {
// Loop through the keys
for(let key in element) {
// Check if a key is not exist in the result
if(!result.hasOwnProperty(key)) {
// Then create an key and assign an empty array to it
result[key] = [];
}
// Push the elemnts to the array.
result[key].push(element[key]);
}
});
I have an array of objects:
var items = [
{
"id":"sugar",
"type": 'eatables'
},
{
"id":"petrol",
"type": 'utility'
},
{
"id":"apple",
"type": 'fruits'
},
{
"id":"mango",
"type": 'fruits'
},
{
"id":"book",
"type": 'education'
}
];
Now I have another array of orders with the help of which I want to sort items array:
var orders = [
{
"id":"sugar",
"order":5
},
{
"id":"book",
"order":1
}
];
Now what I am trying so far in my logic is that I am putting so many loops that it is totally creating mess.
Can anyone suggest me with a short and optimized logic for this?
One approach could be creating one dictionary which will keep the order for every element. Also, I've iterated the whole items array to store the position for the elements that are not in the orders array.
First of all, I'll declare one array which keep the whole orders, thus one array with 1..N elements.
var orderNumbers = Array.from({length: items.length}, (_, v) => v + 1);
Then I started to create the dictionary by iterating orders array and remove orders from orderNumbers.
The last step is iterating the items array and use shift method to "pop" the first element.
The final dictionary will look like
{
"sugar": 2,
"book": 3,
"petrol": 1,
"apple": 4,
"mango": 5
}
In this code I used one dictionary because its lookup has complexity of O(1).
var items = [ { "id":"sugar", "type": 'eatables' }, { "id":"petrol", "type": 'utility' }, { "id":"apple", "type": 'fruits' }, { "id":"mango", "type": 'fruits' }, { "id":"book", "type": 'education' } ], orders = [ { "id":"sugar", "order":2 }, { "id":"book", "order":3 } ], orderNumbers = Array.from({length: items.length}, (_, v) => v + 1);
var ordersDict = orders.reduce((acc, item) => {
acc[item.id] = item.order;
//remove from order numbers
let index = orderNumbers.findIndex(el => el == item.order);
orderNumbers.splice(index, 1);
return acc;
}, {});
for(let i = 0; i < items.length; i++){
if(!ordersDict.hasOwnProperty(items[i].id)){
ordersDict[items[i].id] = orderNumbers[0];
orderNumbers.shift();
}
}
//sort the array
items.sort((a,b) => ordersDict[a.id] - ordersDict[b.id]);
console.log(items);
let oorder = new Object();
orders.map(item=>{oorder[item.id]=item.order});
var new_items = [];
items.map(item=>{new_items[oorder[item.id]-1]=item});
I am trying to loop through a following nested object and get an output as below:
const preference = {
"ethnicity": {
"value": "Gurung",
"rank": 1
},
"occupation": {
"value": "Banker",
"rank": 2
}
}
I tried following:
let preferenceRank = {};
preference.map(pref => {
preferenceRank[pref.rank] = pref;
});
console.log(preferenceRank);
I get this error:
"TypeError: preference.map is not a function"...
Output required:
{
1: "ethnicity",
2: "occupation",
}
You can use Object.entries to get keys and values at once (as array of arrays [key, value]):
const preference = {
"ethnicity": {
"value": "Gurung",
"rank": 1
},
"occupation": {
"value": "Banker",
"rank": 2
}
}
const preferenceRank = {}
for (const [key, { rank }] of Object.entries(preference)) {
preferenceRank[rank] = key
}
console.log(preferenceRank)
(By the way, in your code it doesn't make any sense to use map there, since you are not mapping the array to anything, and you ignore the return value of map. You probably wanted forEach instead or, as I used now, a for loop.)
2021 Update
There is now an easier way widely available, using Object.fromEntries, which does the opposite of Object.entries, thereby allowing us to express the whole thing as a mapping operation:
const preferenceRank = Object.fromEntries(
Object.entries(preference).map(([key, { rank }]) => [rank, key])
)
You can use the .entries() function to map over the object.
Object.entries(preference).reduce((out, [key, value]) => {
out[value.rank] = key;
return out;
},{});
Use Object.entries() to get an array of the keys and values of the object. You can then loop over that.
Use forEach if the loop is being done for side effect rather than using the values returned by the callback function.
const preference = {
"ethnicity": {
"value": "Gurung",
"rank": 1
},
"occupation": {
"value": "Banker",
"rank": 2
}
}
let preferenceRank = {};
Object.entries(preference).forEach(([pref, {rank}]) => {
preferenceRank[rank] = pref;
});
console.log(preferenceRank);
You could map the entries and build a new object.
const
preference = { ethnicity: { value: "Gurung", rank: 1 }, occupation: { value: "Banker", rank: 2 } },
result = Object.fromEntries(Object
.entries(preference)
.map(([k, { rank }]) => [rank, k])
);
console.log(result);
This will work.
const preferenceRank = {};
Object.keys(preference).forEach((key) => {
preferenceRank[preference[key]['rank']] = preference[key]['value'];
});
console.log(preferenceRank);
You could map over the keys and add to a result-object the rank/key-objects.
const preference = {
"ethnicity": {
"value": "Gurung",
"rank": 1
},
"occupation": {
"value": "Banker",
"rank": 2
}
}
let res= {};
Object.keys(preference).map((el,key) => {
res[preference[el].rank] = el;
});
console.log(res);
map only works for arrays, you are dealing with an object, what you can is go through the keys of the objects by using
Object.keys(preference)
this will return to you the object keys in an array as the following
["ethnicity","occupation"]
then you can map through it if you want and do your code
const preference = {
"ethnicity": {
"value": "Gurung",
"rank": 1
},
"occupation": {
"value": "Banker",
"rank": 2
}
}
console.log({...Object.keys(preference)})
I am trying to Sum of the key ‘total’ from the following object, but unable to find the way. I have tried to find it by using Object.key
const data = {
"item1": {},
"item2": {
"total": 13,
},
"item3": {},
"item4": {
"total": 12,
}
}
const count = Object.keys(data).map(item => data[item].total);
console.log(count);
This is what I have tried and in consol.log it is printing 13 and 12 but I am not able to do Sum of them. Also, I have tried the reduce method which is suggested in some of the following answers.
Use Object.keys() to loop over the keys of the object and then access the total property of each object:
var data = {
"item1": {
"total": 17
},
"item2": {
"total": 13
},
"item3": {}
};
var sum = 0;
Object.keys(data).forEach(key => sum += data[key].total || 0);
console.log(sum);
You can use Object.values()
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
and Array.prototype.reduce()
The reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value.
const data = {
"item1": {},
"item2": {
"total": 13,
},
"item3": {},
"item4": {
"total": 12,
}
}
var sum = Object.values(data)
.filter(value => Object.keys(value).length !== 0)
.reduce((a,c) => a+c.total, 0);
console.log(sum);
Use Object.values to get all the values in your data.
Then use Array.reduce to calculate the total.
const data = {
"item1": { "total": 17 },
"item2": { "total": 13 }
}
const values = Object.values(data);
console.log("Values:", values);
const total = values.reduce((a, c) => a + c.total, 0);
console.log("Total:", total);
Of course, you don't need the intermediate variable:
const total = Object.values(data).reduce((a, c) => a + c.total, 0);
const sum = Object.keys(yourObject).map(key => yourObject[key].total).reduce((a, b) => a + b, 0);