How to flatten an array inside of an object? - javascript

Basically, I want to flatten an array inside of an object with Javascript (preferably ES6). I'm actually not sure if this is even an issue of flattening, I just want a good way to make this change.
I want to go from this:
{
id: "123",
name: "test",
history: [
{
id: "456",
name: "test2"
},
{
id: "789",
name: "test3"
}
]
}
To this...
{
id: "123",
name: "test"
},
{
id: "456",
name: "test2"
},
{
id: "789",
name: "test3"
}
Essentially in the original object, I have a "history" property that related to that specific object. Any ideas?

You can use destructuring and rest syntax, to separate history and the 1st object, and then combine them to a single array with spread or concat.
const { history, ...obj1 } = {"id":"123","name":"test","history":[{"id":"456","name":"test2"},{"id":"789","name":"test3"}]}
const result = [obj1, ...history]
console.log(result)

try this:
const data = {
"id": "123",
"name": "test",
"history": [
{
"id": "456",
"name": "test2"
},
{
"id": "789",
"name": "test3"
}
]
}
const {id, name, history} = data ;
const result = [{id, name} , ...history];
console.log(result);

Related

Nested filter returning empty array

Below is the data that I am receiving and I am trying to filter so that a new array contains only objects with the desired location.
However, I'm running into an issue where my function is returning [], an empty array.
data:
[
{ data: [[Object], [Object], [Object]], id: 1 },
{ data: [[Object]], id: 2 },
{ data: [[Object], [Object], [Object], [Object]], id: 3 }
];
data[1]:
{"data": [{"name": "Joe", "job": "N/A", "location": "Los Angeles"}], "id": 2}
This is my current function:
const locations = ["Los Angeles", "Chicago"];
...
const filteredData = data.filter((i) =>
i.data.filter((j) => locations.includes(j.location)),
);
return filteredData;
What is wrong and how can I fix this and get it filtering correctly?
In the callback you pass to the Array.filter(), you need to return a boolean value to filter the array. If you do not return anything, the filter returns an empty array.
But in your case, you are returning inner filtered array that returns at least an empty array and the outer filter behaves it as a true value. So the outer filter will return all of the items in the original array. (not an empty one as you stated)
Also you are returning filteredData in a place where it results in a syntax error.
const data = [
{"data": [{"name": "Joe", "job": "N/A", "location": "Los Angeles"}], "id": 2},
{"data": [{"name": "Jane", "job": "N/A", "location": "Charlotte"}], "id": 3},
]
const locations = ["Los Angeles", "Chicago"];
const filteredData = data.filter((i) =>
i.data.filter((j) => locations.includes(j.location)).length > 0,
);
console.log(filteredData);
Another Option is use some() to get your expected result. This way you don't need to loop through all item in data array comparing to filter()
const data = [
{ data: [{ name: "Joe", job: "N/A", location: "Los Angeles" }], id: 2 },
{ data: [{ name: "Jane", job: "N/A", location: "Charlotte" }], id: 3 },
{ data: [{ name: "Sam", job: "N/A", location: "SSS" }], id: 4 },
{
data: [
{ name: "John", job: "N/A", location: "AAA" },
{ name: "Doe", job: "N/A", location: "BBB" },
],
id: 5,
},
];
const locations = ["Los Angeles", "Chicago", "AAA"];
const existData = data.filter(el =>
el.data.some(item => locations.includes(item.location))
);
console.log(existData);
If you also want to filter the data array, you can do like below.
const data = [
{ data: [{ name: "Joe", job: "N/A", location: "Los Angeles" }], id: 2 },
{ data: [{ name: "Jane", job: "N/A", location: "Charlotte" }], id: 3 },
{ data: [{ name: "Sam", job: "N/A", location: "SSS" }], id: 4 },
{
data: [
{ name: "John", job: "N/A", location: "AAA" },
{ name: "Doe", job: "N/A", location: "BBB" },
],
id: 5,
},
];
const locations = ["Los Angeles", "Chicago", "AAA"];
const filteredData = data.reduce((acc, cur) => {
const filteredItem = cur.data.filter(item => locations.includes(item.location));
if (filteredItem.length) {
acc.push({ ...cur, data: filteredItem });
}
return acc;
}, []);
console.log(filteredData);

Combine and flatten elements of array

With the below JSON content that is actually coming from an API but I'm using a json file for testing. I would like to combine the primary key and flatten the ItemList.
[{
"PrimaryKey": "123",
"ItemList": [
{
"SecondaryKey": "ABC",
"Name": "Item1",
"Description": "Sample item"
},
{
"SecondaryKey": "DEF",
"Name": "Item2",
"Description": "Another sample item"
}
],
"IgnoreThis": [
{
"SomeData": "Some Data"
}
]
}]
The output I would like is:
[{
"PrimaryKey": 123,
"SecondaryKey": "ABC",
"Name": "Item1",
"Description": "Sample Item"
},
{
"PrimaryKey": 123,
"SecondaryKey": "DEF",
"Name": "Item2",
"Description": "Another sample item"
}]
I've got the Item list being flattened by:
let items = [];
items.push(JSON.parse(fs.readFileSync('./items.json')));
let result = items.reduce((r, obj) => r.concat(obj.ItemList), []);
I've tried to use items.map to get the desired output nothing has worked, I don't think I understand how to chain .map and .reduce effectively as I get undefined as the result.
Any ideas how I can achieve this output?
You can do this by running map twice: get the PrimaryKey from the first map, then add it to all the objects inside the second map, then you flatten the array you got from the previous stage.
const data = [
{
PrimaryKey: "123",
ItemList: [
{
SecondaryKey: "ABC",
Name: "Item1",
Description: "Sample item",
},
{
SecondaryKey: "DEF",
Name: "Item2",
Description: "Another sample item",
},
],
IgnoreThis: [
{
SomeData: "Some Data",
},
],
},
{
PrimaryKey: "456",
ItemList: [
{
SecondaryKey: "ABC",
Name: "Item1",
Description: "Sample item",
},
{
SecondaryKey: "DEF",
Name: "Item2",
Description: "Another sample item",
},
],
IgnoreThis: [
{
SomeData: "Some Data",
},
],
},
];
const result = data.map(({ PrimaryKey, ItemList }) => ItemList.map(item => ({
PrimaryKey,
...item,
}))).flat();
console.log(result);

I'm struggling with Javascript Object manipulation to output the desired result

I have an array of objects that I'd like to group into particular keys and format.
Here is the Object I have:
{
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
My aim is to try and get the Object into the following format
{
"id":"98765",
"things":{
"123456":{
"54353":{
"val":"file1"
}
},
"1234":{
"3453":{
"val":"file2"
},
"5433":{
"val":"file3"
}
}
}
}
I have managed to achieve getting the 'clientId' to be a key of the Objects, but am struggling to get the campaign IDs to be nested within that Object.
This is the code I've tried
const a = obj.things.reduce((ac, {clientId, ...rest})=> (ac[clientId] = rest, ac), {})
console.log(a);
This gives a format of the following which obviously doesn't achieve the cmpId nesting, but also gets rid of one of my Objects as the clientId is a duplicate. I'm a bit lost about how I achieve something quite neat without horrible reams of code to achieve it.
{
'1234': { val: 'file3', cmpId: '5433' },
'123456': { val: 'file1', cmpId: '54353' }
}
You were almost there, you also need to get the cmpId and then check if there is already an object for clientId, here is an example:
const input = {
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
input.things = input.things.reduce((a, {
clientId,
cmpId,
...rest
}) => {
if (a[clientId]) {
a[clientId][cmpId] = rest;
} else {
a[clientId] = {
[cmpId]: rest
};
}
return a;
}, {});
console.log(input);
You're close. Using Array#reduce is the right approach. You just need to change ac[clientId] = rest to the structure you want:
const obj = {
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
obj.things = obj.things.reduce( ( res, { clientId, cmpId, ...rest } ) => {
res[clientId] = { ...res[clientId], [cmpId]: rest };
return res;
}, { } );
console.log( obj );
You could use .reduce() to map/combine the things property, like so:
const data = { "id": "98765", "things": [{ "clientId": "123456", "val": "file1", "cmpId": "54353" }, { "clientId": "1234", "val": "file2", "cmpId": "3453" }, { "clientId": "1234", "val": "file3", "cmpId": "5433" } ] };
const newData = { id: data.id,
things: data.things.reduce((out, {clientId, cmpId, ...rest}) => //for each "thing"
({...out, [clientId]: {...out[clientId], [cmpId]: rest}}) //Create/merge with existing clientId
, {})
};
console.log(newData);
This version is similar to several others here. It's designed to avoid mutating your original structure and the allow additional properties at both levels to be passed through to the output, caring only about things, clientId, and cmpId.
const transform = ({things, ...rest}) => ({
...rest,
things: things.reduce(
(a, {clientId, cmpId, ...rest}) => ({...a, [clientId]: {...a[clientId], [cmpId]: rest}}),
{}
)
})
const data = {id: "98765", things: [{clientId: "123456", val: "file1", cmpId: "54353"}, {clientId: "1234", val: "file2", cmpId: "3453"}, {clientId: "1234", val: "file3", cmpId: "5433"}]}
console .log (
transform (data)
)
try using JSONPath incase you are aimimg to get your desired result by iterating over the the object.It is an excellent tool which basically coverts the whole json into XML

Simultaneously grouping and transforming data in JavaScript (with Lodash)

Given the following data set:
const users = {
"1": { id: "1", name: "Alex" },
"2": { id: "2", name: "John" },
"3": { id: "3", name: "Paul" }
};
const memberships = [
{ userId: "1", groupId: "1" },
{ userId: "2", groupId: "2" },
{ userId: "3", groupId: "1" }
];
What is an effective way to achieve following desired result?
const usersByGroupId = {
"1": [{ id: "1", name: "Alex" }, { id: "3", name: "Paul" }],
"2": [{ id: "2", name: "John" }]
}
I came up with the following (using Lodash):
const usersByGroupId = mapValues(
groupBy(memberships, "groupId"),
memberships => memberships.map(membership => users[membership.userId])
);
I'm not that familiar with big O notation, but I can imagine the performance of the above solution is pretty terrible on large sets. Any suggestions for improvement?
You don't really need lodash — you can do this in one step with reduce(). Just check if the key exists, if so push, if not set a new array and push. It just requires one iteration of the membership array and for each a lookup in the users object (which is more-or-less constant time) making this a linear time operation.
const users = {"1": { id: "1", name: "Alex" },"2": { id: "2", name: "John" },"3": { id: "3", name: "Paul" }};
const memberships = [{ userId: "1", groupId: "1" },{ userId: "2", groupId: "2" },{ userId: "3", groupId: "1" }];
let groups = memberships.reduce((obj, {userId, groupId}) => {
(obj[groupId] || (obj[groupId] = []) ).push(users[userId])
return obj
}, {})
console.log(groups)

Create multiple object looping over array

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.

Categories