loop through nested object javascript - javascript

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)})

Related

Grouping elements of array on the basis of property

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}

how to update a particular json data object which belongs to browser local-storage

This is Browser localstorage Object referred as dataset
let dataset = localStorage.getItem('dataset') !== null ? leech : [];
[
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
This is the initial data object available I want to add more field to a particular id.
This is what I want :
[
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd",
"status":1
}
]
This my code to find the particular id
const user = dataset.find(user => user.id == 456);
Now how can I add status to user and update the user in the dataset?
You've already found the user by using Array.prototype.find() so all you need to do then is add the status property
// const dataset = JSON.parse(localStorage.getItem("dataset"))
const dataset = [{"id":123,"name":"abc"},{"id":456,"name":"bcd"}]
const user = dataset.find(({ id }) => id === 456)
if (user) {
user.status = 1
}
console.info(dataset)
.as-console-wrapper { max-height: 100% !important }
If you then want to store the modified data back into localStorage, use localStorage.setItem() and JSON.stringify()
localStorage.setItem("dataset", JSON.stringify(dataset))
If you want keep dataset initial value, and would like to get a new array, you can use Array.reduce() method.
const dataset = [
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
const output = dataset.reduce((acc, cur) => {
if (cur.id === 456) cur.status = 1;
acc.push(cur);
return acc;
}, []);
console.log(output);
If you want to update dataset, you can use Array.forEach() method.
const dataset = [
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
dataset.forEach(user => {
if (user.id === 456) user.status = 1;
});
console.log(dataset);
You could do with Array#Findindex with callback return function. so could pass the originaldata,searchId and update object. In this method you could updated object easily
Why i suggest findIndex
Because findindex not running entire loop or iteration. If the match
detect on first iteration they will break the loop and returning the
result.For long iteration its more faster than other loop (reduce,forEach)
const data = [ { "id": 123, "name": "abc" }, { "id": 456, "name": "bcd" } ]
function update(dataset,searchId,addtionObject){
let ind = dataset.findIndex(({id}) => id == searchId);
dataset[ind] = {...dataset[ind],...addtionObject}; //join the new and old array
return dataset
}
console.log(update(data,456,{status:1}))
If you want to create new state objet, you can use immer for that.
Immer will produce the nextState based on the mutations to the draft state.
import produce from "immer";
const baseState = [
{
id: 123,
name: "abc",
},
{
id: 456,
name: "bcd",
},
];
const nextState = produce(baseState, (draftState) => {
draftState[1].status = 1;
});

How to access an object nested inside an array by the value of one of its keys? [duplicate]

This question already has answers here:
Filtering object properties based on value
(8 answers)
Closed 3 years ago.
Say I have an object that looks like this:
{
"data": {
"postsConnection": {
"groupBy": {
"author": [
{
"key": "xyz",
"connection": {
"aggregate": {
"count": 5
}
}
},
{
"key": "abc",
"connection": {
"aggregate": {
"count": 3
}
}
}
]
}
}
}
}
How would one access the value of count corresponding to the author element that has, say, xyz as its key? I know for this particular example I could just do this:
const n = data.postsConnection.groupBy.author[0].connection.aggregate.count
But that would mean knowing in advance which element in the array holds the desired value for key, which isn't the case in my context.
If the author can appear multiple times, you can .filter() the array stored at author and then .map() the results to the count:
const data = {data:{postsConnection:{groupBy:{author:[{key:"xyz",connection:{aggregate:{count:5}}},{key:"abc",connection:{aggregate:{count:3}}}]}}}};
const author = "xyz";
const res = data.data.postsConnection.groupBy.author.filter(({key}) => key === author).map(obj => obj.connection.aggregate.count);
console.log(res);
// If you want to get the total of all counts for the given author, you can use reduce on the result to sum:
const totalCountsForAuthor = res.reduce((acc, n) => acc+n, 0);
console.log(totalCountsForAuthor);
If the author can only appear once, you can use .find() instead of .filter() like so:
const data = {data:{postsConnection:{groupBy:{author:[{key:"xyz",connection:{aggregate:{count:5}}},{key:"abc",connection:{aggregate:{count:3}}}]}}}};
const author = "xyz";
const res = data.data.postsConnection.groupBy.author.find(({key}) => key === author).connection.aggregate.count
console.log(res);
You can use Array#find to get the first instance inside an array that meets a certain condition (in your case, the first instance whose key value is equal to the key value you want).
var obj = {"data":{"postsConnection":{"groupBy":{"author":[{"key":"xyz","connection":{"aggregate":{"count":5}}},{"key":"abc","connection":{"aggregate":{"count":3}}}]}}}};
function getAuthorByKey(key) {
return obj.data.postsConnection.groupBy.author.find(author => author.key === key);
}
console.log(getAuthorByKey("xyz").connection.aggregate.count);
console.log(getAuthorByKey("abc").connection.aggregate.count);
If the author array always exists:
const data = {
postsConnection: {
groupBy: {
author: [{
key: "xyz",
connection: {
aggregate: {
count: 5
}
}
}, {
key: "abc",
connection: {
aggregate: {
count: 3
}
}
}]
}
}
};
function getCount(keyVal) {
const element = data.postsConnection.groupBy.author.find(item => item.key === keyVal)
return element.connection.aggregate.count || "";
}
console.log(getCount('xyz'))
var data = { "data": {
"postsConnection": {
"groupBy": {
"author": [
{
"key": "xyz",
"connection": {
"aggregate": {
"count": 5
}
}
},
{
"key": "abc",
"connection": {
"aggregate": {
"count": 3
}
}
}
]
}
}
}
};
data.data.postsConnection.groupBy.author.forEach((autor) => {
if(autor.key === "xyz")
console.log(autor.connection.aggregate);
});
You can make use of array find, to find the author by "key".
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
const author = data.postsConnection.groupBy.author.find((author) => author.key === "xyz")
CodePen:
https://codepen.io/gmaslic/pen/wvwbLoL
Then you can access all the properties from found "author"

Convert an array of objects to a dictionary by letter [duplicate]

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
Closed 3 years ago.
I have an array of objects that looks like this:
let stuff = [
{
"id": "48202847",
"name": "Doe"
},
{
"id": "17508",
"name": "Marie"
},
{
"id": "175796",
"name": "Robert"
},
{
"id": "175796",
"name": "Ronald"
},
]
What I want to get is a dictionary looking something like this:
{
"D": [{"id": "48202847", "name": "Doe"}],
"M": [{"id": "17508", "name": "Marie"}],
"R": [{"id": "175796", "name": "Robert"}, {"id": "175796", "name": "Ronald"}]
}
Notice how all the people whose name starts with "R" are listed under one key.
This is my function that creates a dictionary with the person's name as the key:
const byId = (array) =>
array.reduce((obj, item) => {
obj[item.name] = item
return obj
}, {})
But this obviously doesn't do what I want it to. I do have some ideas of how to make this possible, but they are extremely legacy and I would love to know how to do this right.
Any help is appreciated!
You need the first character, uppercase and an array for collecting the objects.
const byId = array =>
array.reduce((obj, item) => {
var key = item.name[0].toUpperCase(); // take first character, uppercase
obj[key] = obj[key] || []; // create array if not exists
obj[key].push(item); // push item
return obj
}, {});
let stuff = [{ id: "48202847", name: "Doe" }, { id: "17508", name: "Marie" }, { id: "175796", name: "Robert" }, { id: "175796", name: "Ronald" }],
result = byId(stuff)
console.log(result);
Here's a solution based on Set, map, reduce and filter:
let stuff = [{"id": "48202847","name": "Doe"},{"id": "17508","name": "Marie"},{"id": "175796","name": "Robert"},{"id": "175796","name": "Ronald"}];
let result = [...new Set(stuff.map(x => x.name[0]))]
.reduce((acc, val) => {
return acc = { ...acc,
[val]: stuff.filter(x => x.name.startsWith(val))
}
}, {});
console.log(result);
Great solution Nina! Could be made a little cleaner by utilizing the spread operator.
const byId = (array) =>
array.reduce((obj, item) => {
var key = item.name[0].toUpperCase();
return {
...obj,
[key]: obj[key] ? [...obj[key], item] : [item],
}
}, {});

Add fields from array of JSON objects based on specific field

I have an array of objects ...
[
{
"matchID":"-LP0LKl_nR4VQf6Gxwz8",
"playerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points":"11",
"id":"-LP1WvT4eN1L7BLbyhJt"
},
{
"matchID":"-LP0LKl_nR4VQf6Gxwz8",
"playerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points":"23",
"id":"-TP1WvT4eN1L7GeYyhJt"
},
{
"matchID":"-DF0LKl_nR4VQf6Gxwz7",
"playerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points":"12",
"id":"-GH1WvT4eN1L7GeYyhJt"
},
{
"matchID":"-DF0LKl_nR4VQf6Gxwz7",
"playerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points":"6",
"id":"-XZ1WvR2eN1L7GeYyhJt"
}
]
I want to loop through this array and create a new array that is a basically just each playerId and their total points for all matches.
So after cycling through the above, the array would look like ..
[{"palyerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6", "points": "35"},{"palyerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3", "points": "17"}]
You can use reduce() to loop thru the array. Use new Map() to group the array. And use spread operator to convert the map object into an array.
var arr = [{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"11","id":"-LP1WvT4eN1L7BLbyhJt"},{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"23","id":"-TP1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"12","id":"-GH1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"6","id":"-XZ1WvR2eN1L7GeYyhJt"}]
var result = [...arr.reduce((c, v) => {
if (!c.has(v.palyerId)) c.set(v.palyerId, {"palyerId": v.palyerId,"points": 0});
c.get(v.palyerId).points += +v.points;
return c;
}, new Map()).values()];
console.log(result);
Or you can reduce() the array into an object using the palyerId as the key. Use Object.values() to convert the object into an array.
var arr = [{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"11","id":"-LP1WvT4eN1L7BLbyhJt"},{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"23","id":"-TP1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"12","id":"-GH1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"6","id":"-XZ1WvR2eN1L7GeYyhJt"}]
var result = Object.values(arr.reduce((c, {palyerId,points}) => {
c[palyerId] = c[palyerId] || {palyerId,points: 0};
c[palyerId].points += +points;
return c;
}, {}));
console.log(result);
A non reduce version with a for loop looks like the following (again, the central idea is to group by playerId):
let data = [{
"matchID": "-LP0LKl_nR4VQf6Gxwz8",
"playerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points": "11",
"id": "-LP1WvT4eN1L7BLbyhJt"
},
{
"matchID": "-LP0LKl_nR4VQf6Gxwz8",
"playerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points": "23",
"id": "-TP1WvT4eN1L7GeYyhJt"
},
{
"matchID": "-DF0LKl_nR4VQf6Gxwz7",
"playerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points": "12",
"id": "-GH1WvT4eN1L7GeYyhJt"
},
{
"matchID": "-DF0LKl_nR4VQf6Gxwz7",
"playerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points": "6",
"id": "-XZ1WvR2eN1L7GeYyhJt"
}
];
var res = {};
for (let d of data) {
if (d.playerId in res) res[d.playerId].points += parseInt(d.points);
else res[d.playerId] = {playerId: d.playerId, points: parseInt(d.points)};
}
console.log(Object.values(res));

Categories