So i have an array which stores hobbies for each user in an array within the object..
var hobbies = [
{
"id": 1,
"hobbies": []
},
{
"id": 2,
"hobbies": [
"football"
]
},
{
"id": 3,
"hobbies": [
"football",
"basketball"
]
}
]
What i want to return is a new array of objects but each hobby separated into their own object like below.
var result = [
{
"id": 2,
"hobby": "football"
},
{
"id": 3,
"hobby": "football"
},
{
"id": 3,
"hobby": "basketball"
}
]
What is have so far is
hobbies.filter((f, i) => f.hobbies.length > 0).map((p, i) => {
while (i < p.hobbies.length) {
return { id : p.id, hobby : p.hobbies[i] };
}
});
which only returns
[
{
"id": 2,
"hobby": "football"
},
{
"id": 3,
"hobby": "basketball"
}
]
You can use array#reduce with array#map. Iterate through each object and then iterate through each hobby of hobbies and create the object.
var hobbies = [ { "id": 1, "hobbies": [] }, { "id": 2, "hobbies": [ "football" ] }, { "id": 3, "hobbies": [ "football", "basketball" ] } ],
result = hobbies.reduce((r, {id, hobbies}) => r.concat(hobbies.map(hobby => ({id, hobby}))), []);
console.log(result);
I know, "functional" programming is considered "cool" around these parts, however, have you considered using simple loops to, well, loop over your data?
let result = [];
for (let {hobbies, id} of data)
for (let hobby of hobbies)
result.push({id, hobby})
In my opinion, this is far more readable than any reduce spaghetti one could come up with ;)
You need to use inner-loop to loop through the hobbies and push them one-by-one to the target array:
var hobbies = [{
"id": 1,
"hobbies": []
},
{
"id": 2,
"hobbies": [
"football"
]
},
{
"id": 3,
"hobbies": [
"football",
"basketball"
]
}
];
var result = hobbies.reduce((acc, item) => {
item.hobbies.forEach(hobby => {
acc.push({
id: item.id,
hobby: hobby
});
});
return acc;
}, []);
console.log(result);
You can use array.prototype.reduce:
var hobbies = [{"id": 1,"hobbies": []},{"id": 2,"hobbies": ["football"]},{"id": 3, "hobbies": ["football","basketball"]}];
var res = hobbies.reduce((m, o) => (o.hobbies.forEach(h => m.push({id: o.id, hobby: h})), m), []);
console.log(res);
You need nested loops and this is the basics of it:
You first need to loop over the main hobbies array.
Then for each item in the array (which represents a person), you want to loop through their hobbies, and for each one of those hobbies, you need to push an object made up of the profile ID and the hobby into results array I created earlier.
var hobbies = [{ "id": 1, "hobbies": [] }, { "id": 2, "hobbies": [ "football" ] }, { "id": 3, "hobbies": [ "football", "basketball" ] } ];
let result = [];
hobbies.forEach(function(profile){
profile.hobbies.forEach(function(hobby){
result.push(
{
"id": profile.id,
"hobby": hobby
}
);
});
});
console.log(result)
Update: the other answers with Array.reduce (a more specialised loop) will cut the above code down even further.
Related
This question already has answers here:
How to remove all duplicates from an array of objects?
(77 answers)
Closed 4 months ago.
If the 'id' key is duplicated among the objects in the array, how to delete the object
I tried using filter, map, and set, but it doesn't work.
It's not a one-dimensional array, so I don't know how to do it.
as-is
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345",
...
},
{
"id": "12345",
...
},
{
"id": "67890",
...
},
]
}
to-be
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345",
...
},
{
"id": "67890",
...
},
]
}
We iterate over that list using reduce function, then we checked whether the key we are accessing is visited or not with keys parameter of reduce method, and if it's not visited then we just push that object to a filtered array and returning keys array to keep it updated.
const data = {
"category": {
"key": 1,
"order": 1,
"list": [{
"id": "12345"
},
{
"id": "12345"
},
{
"id": "67890"
},
]
}
}
let filtered = [];
data.category.list.reduce((keys, currentObject) => {
if (!keys.includes(currentObject.id)) { //checking if current oject id is present in keys or not
// if not present than we will just push that object in
keys.push(currentObject.id);
//getting filttered object
filtered.push(currentObject);
}
return keys; //returning keys to update it
}, [])
data.category.list = filtered; //updating list
console.log(data);
A solution based on #Nick's comment
let data ={
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345"
},
{
"id": "12345"
},
{
"id": "67890"
},
]
}
}
let uniq = data.category.list.filter((o,i,a) => a.findIndex(o2 => o2.id == o.id) == i)
data.category.list = uniq
console.log(data)
You can use a set to track if id
const category = [{
"category": {
"key": 1,
"order": 1,
"list": [{
"id": "12345",
},
{
"id": "12345",
},
{
"id": "67890",
},
]
}
}]
const z = category.map(elem => {
const set = new Set()
return {
...elem,
category: {
...elem.category,
list: elem.category.list.reduce((acc, curr) => {
if (!set.has(curr.id)) {
set.add(curr.id);
acc.push(curr)
}
return acc;
}, [])
}
}
});
console.log(z)
I have two arrays as follow:
const categories = [
{"itemId": [1], "menuItemCateName": "Popular"},
{"itemId": [1, 2], "menuItemCateName": "Featured"}
]
const items = [
{
"Id": 1,
"price": 10,
"itemName": "Spicy burger"
},
{
"Id": 2,
"price": 10,
"itemName": "Pizza"
}
]
I want to filter the data based on itemId inside array categories so the result will looks like
[
{ name: 'Popular', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] },
{ name: 'Featured', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"},{ "Id": 2, "price": 10, "itemName": "Pizza"} ] }
]
What i tried is:
let data = []
categories.map(category => {
items.filter(item => item.Id === category.itemId[0]).map(b => {
data.push({ 'name': category.menuItemCateName, 'items': [b] })
})
})
But I am getting this result:
[
{ name: 'Popular', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] },
{ name: 'Featured', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] }
]
In the Featured category there should be two items data, but i am getting one item data because I am doing category.itemId[0] inside the filter function, which i have to loop inside the value of property itemId which i have no idea how to accomplish it.
I will appreciate any help.
Using map() to create new object and using filter() to add only matching itemIds
const categories = [
{"itemId": [1], "menuItemCateName": "Popular"},
{"itemId": [1, 2], "menuItemCateName": "Featured"}
]
const items = [
{
"Id": 1,
"price": 10,
"itemName": "Spicy burger"
},
{
"Id": 2,
"price": 10,
"itemName": "Pizza"
}
]
let result = categories.map(cat => {
let newObject = { // creating new object with desired properties
name: cat.menuItemCateName,
items: items.filter(item => cat.itemId.some(s => s === item.Id)) // filtering matching its object
}
return newObject;
})
console.log(result)
console.log('********* second object items have to values: ',result[1].items.length )
console.log(result[1].items)
I have an array that contains a lot of base attributes and multiple nested arrays. I'm trying to remove the nested arrays leaving all the base items. I don't want to filter them out by name but type.
{
"ID": 123,
"ItemName": "Item1",
"CommentList": [
{
"CommentID": "ABC",
"CommentText": "Test entry"
}
],
"ExtraList": [
"A123B"
]
}
The desired outcome is:
{
"ID": 123,
"ItemName": "Item1",
}
I tried filter but couldnt get it to work, the below is the closest I've come but given the fact I can't gracefully return the property back to the caller this must be the wrong track.
let test = arr.forEach(function (item) {
for (var propertyName in item) {
if (!Array.isArray(propertyName))
return propertyName;
}
});
For starters, Array.prototype.forEach does not return a new array. Please use Array.prototype.map instead if you would like to return a new array.
I am only saying this because you are assigning it to test, which would return undefined.
With that out of they way this should work
data.forEach(item => {
for(const key in item) {
if(Array.isArray(item[key])) {
delete item[key]
}
}
})
Here is the code that input and output:
Input:
const data = [
{
"ID": 123,
"ItemName": "Item1",
"CommentList": [
{
"CommentID": "ABC",
"CommentText": "Test entry"
}
],
"ExtraList": [
"A123B"
]
},
{
"ID": 124,
"ItemName": "Item1",
"CommentList": [
{
"CommentID": "ABC",
"CommentText": "Test entry"
}
],
"ExtraList": [
"A123B"
]
}
]
Output:
[
{ID: 123, ItemName: "Item1"},
{ID: 124, ItemName: "Item1"}
]
Hope this helps!
Try this
const a = {
"ID": 123,
"ItemName": "Item1",
"CommentList": [
{
"CommentID": "ABC",
"CommentText": "Test entry"
}
],
"ExtraList": [
"A123B"
]
}
const obj = {};
Object.keys(a)
.filter(key => !(a[key] instanceof Array))
.forEach(key => obj[key] = a[key]);
console.log(obj);
arr is an object, you can get an arrays of keys with Object.keys(obj) an then filter it.
let obj = {
"ID": 123,
"ItemName": "Item1",
"CommentList": [{
"CommentID": "ABC",
"CommentText": "Test entry"
}],
"ExtraList": [
"A123B"
]
}
let test = Object.keys(obj).filter((key) => {
return !Array.isArray(obj[key])
})
console.log(test)
You can Do this way, The easiest way to Do this
const data = [
{
"ID": 123,
"ItemName": "Item1",
"CommentList": [
{
"CommentID": "ABC",
"CommentText": "Test entry"
}
],
"ExtraList": [
"A123B"
]
},
{
"ID": 124,
"ItemName": "Item2",
"CommentList": [
{
"CommentID": "CDE",
"CommentText": "Test entry 2"
}
],
"ExtraList": [
"A123BC"
]
}
]
let finalOutput=JSON.parse(JSON.stringify(data,['ID','ItemName']))
console.log(finalOutput)
I have this object structure:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
I'd like to change it so it appears like this:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"suppliers":[
{
"supplier_id": 1,
"supplier_name": [
"Supplier1"
]
}
],
"supplier_code": "SUP001",
"count": "21"
}
}
I tried this hoping it would work:
const group = accumulator[item.id];
group.suppliers = [];
group.suppliers = group.suppliers.push(item.supplier_name, item.supplier_id, item.supplier_code);
return accumulator;
Unfortunately that just seems to give me a count of the objects pushed into suppliers, suppliers isn't an array and supplier_id, supplier_name and supplier_code are still visible outside of suppliers:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21",
"suppliers: 3
}
}
How do I change it to the format I want?
You could use es6 Destructuring assignment, Object.values es2017 (or Object.keys instead).
If you assume that users contains more then one user you could use reduce.
In the example below original object won't be mutated.
Hope it helps
const original = {
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
};
const { users } = original;
const reshaped = Object.values(users).reduce((acc, { id, supplier_id, supplier_name, ...rest }) => {
acc[id] = {
...rest,
suppliers: [{
supplier_id,
supplier_name: [supplier_name]
}]
};
return acc;
}, {});
console.log(reshaped);
You need to use an object to push into the suppliers array. Also, delete the old keys which are not needed.
Edit - You can directly create an array of 1 object. Thanks #Adam
const group = accumulator[item.id];
group.suppliers = [{
supplier_id: item.supplier_id,
supplier_name: item.supplier_name,
supplier_code: item.supplier_code
}];
delete group.supplier_id;
delete group.supplier_name;
delete group.supplier_code;
return accumulator;
Here's a quick and modern solution:
const parseUsers = (users) => {
let parsedUsers = {};
for (key in users) {
const user = users[key];
// destructuring (or extracting) the relevant keys from the . user object, keeping everything else under 'rest'
const { supplier_id, supplier_name, ...rest } = user;
parsedUsers[key] = {
...rest, // spreading back our rest params
suppliers: [ // creating a new array and populating it with the keys which we previously extracted (along with their corresponding values)
supplier_id,
supplier_name
]
}
}
return parsedUsers;
}
usage: parseUsers(json.users)
Today I was working on a problem, which states as follows:
Problem:
INPUT: [{..}, {..}, ..] Array of objects;
Each object is has {"id": required, "children": []}
The objects has parent-child relation based on "id" and "children" props
OUTPUT: [{..}, {..}, ..] Array in a tree (hierarchy) order :multi-level.
Input:
[{
"id": 1,
"name": "Earth",
"children": [2, 3]
}, {
"id": 2,
"name": "Asia",
"children": []
}, {
"id": 3,
"name": "Europe",
"children": [4]
}, {
"id": 4,
"name": "Germany",
"children": [5]
}, {
"id": 5,
"name": "Hamburg",
"children": []
}]
OutPut
[{
"id": 1,
"name": "Earth",
"children": [{
"id": 2,
"name": "Asia",
"children": []
}, {
"id": 3,
"name": "Europe",
"children": [{
"id": 4,
"name": "Germany",
"children": [{
"id": 5,
"name": "Hamburg",
"children": []
}]
}]
}]
}]
My approach
I decided to solve this by iterating through each element in the array and recursively find and append objects to children of each element.
So just to start with, I decided to have only First level children appended their respective parents. And my code is following.
var posts = [{"id":1,"name":"Earth","children":[2,3]},{"id":2,"name":"Asia","children":[]},{"id":3,"name":"Europe","children":[4]},{"id":4,"name":"Germany","children":[5]},{"id":5,"name":"Hamburg","children":[]}]
function getElementById (id, posts) {
for(var i =0; i< posts.length; i++){
if(posts[i].id === id){
var found = posts[i];
///// FUN here -> //// posts.splice(i, 1);
return found;
}
}
}
function refactorChildren(element, posts) {
if(!element.children || element.children.length === 0) {
return element;
}
var children = [];
for(var i = 0; i < element.children.length; i++){
var childElement = getElementById(element.children[i], posts);
children.push(childElement);
}
element.children = children;
return element;
}
function iterate(posts) {
var newPosts = [];
var des = [...posts]
for(var i = 0; i < des.length; i++){
var childedElement = refactorChildren(des[i], des);
newPosts.push(childedElement);
}
return newPosts;
}
var filtered = iterate(posts);
console.log(JSON.stringify(filtered))
Surprisingly above code Solves the ACTUAL PROBLEM (except a lil bit of more work)
My Expected Result should be the following: Array of objects with only First level children
[{
"id": 1,
"name": "Earth",
"children": [{
"id": 2,
"name": "Asia",
"children": []
}, {
"id": 3,
"name": "Europe",
"children": [4]
}]
}, {
"id": 4,
"name": "Germany",
"children": [{
"id": 5,
"name": "Hamburg",
"children": []
}]
}]
And I do get the above result if I uncomment the ///// FUN here -> //// line. Which is erasing the iterating object on the go.
So my problem is
I want to know - HOW DID? All the objects got appended correctly to their respective Parent objects by that code? My next step was to add a recursion call to the function refactorChildren(with-childElement).
AND
How did, just by adding posts.splice(i, 1); got me MY expected result from the code?
Please help me understand, I just cant go ahead without knowing "HOW".
Thanks
While traversing the objects, you recursively call a function on all its chilfren and remove the objects from the array:
[
{ id: 1, children: [2], }, // < iterator
{ id: 2, children: [] }, // < gets spliced out recursively
]
If a child is in the array before its parent however, this won't work as you copy the child into another array before the parent gets visited.
Maybe you are interested in a different approach with only a single loop for getting the parent elements and their children.
This works for unsorted data, too.
var data = [{ id: 1, name: "Earth", children: [2, 3] }, { id: 2, name: "Asia", children: [] }, { id: 3, name: "Europe", children: [4] }, { id: 4, name: "Germany", children: [5] }, { id: 5, name: "Hamburg", children: [] }],
tree = function (array) {
var r = {},
children = new Set,
result = [];
array.forEach(o => {
Object.assign(
r[o.id] = r[o.id] || {},
o,
{ children: o.children.map(id => (children.add(id), r[id] = r[id] || {})) }
);
});
return Object.values(r).filter(({ id }) => !children.has(id));
}(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }