Transform javascript array with objects with lodash - javascript

I'm wondering what the best way would be to transform my javascript array with objects. I have tried making a fancy chain with lodash but I can't figure it out.
I need to format the data this way because of the way the backend works.
// from:
var something = [
{
name: 'foo',
stuff: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
},
{
name: 'bar',
stuff: []
},
{
name: 'baz',
stuff: [
{
id: 7
},
{
id: 8
}
]
}
];
// to:
var transformed = [
{
name: 'foo',
included: {
included: [1, 2, 3]
}
},
{
name: 'bar',
included: {
included: []
}
},
{
name: 'baz',
included: {
included: [7, 8]
}
}
];

You can do this quite concisely with two map calls (the array built in or lodash's map), one nested to handle the "included" array within each object:
const transformed = something.map(it => {
return {
name: it.name,
included: {
included: it.stuff.map(thing => thing.id)
}
};
});

No need for lodash, just use the Array.prototype.map function:
// Sorry no fancy ES6 => here :S
var res = something.map(function(item) {
item.included = {included : item.stuff.map(function(i) {return i.id})}
delete(item.stuff)
return item
})
Per #ssube's comment:
var res = something.map(function(item) {
return {
included : {included : item.stuff.map(function(i) {return i.id})},
name: item.name
}
})
See this fiddle

Related

Filtering an array based object's property inside a method

I have the following two simple objects:
clinics: [
{
id: 1,
name: 'New Hampshire Veterinarian Clinic',
plans: [
'handle123',
'handle567',
]
},
{
id: 2,
name: 'Westminster Moltchester Clinic',
plans: [
'handle123',
'handle789',
]
}
],
animals: [
{
id: 1,
handle: 'handle123',
name: 'Cat',
},
{
id: 2,
handle: 'handle567',
name: 'Dog',
},
{
id: 3,
handle: 'haneld789',
name: 'Horse'
}
],
I have the following method:
updateAnimals(selectedOption, id) {
}
where selectedOption is one object from the clinics array.
I want to filter the second array so it only contains the handles mentioned in the selected option, but I'm having trouble with the arguments. I want to achieve something like this:
updateAnimals(selectedOption, id) {
let filteredAnimals = this.animals.filter(function({id, handle, name}) {
// Access the selectedOption here so I can use it to filter
});
}
But I'm not sure how to access the selected option inside the function...
Or is there a better way to do that?
You can use selectedOption just like any other variable available in a scope by simply using it directly selectedOption.something:
updateAnimals(selectedOption, id) {
let filteredAnimals = this.animals.filter({ handle } => {
return selectedOption.plans.includes(handle)
});
}
updateAnimals(selectedOption, id) {
const handles=selectedOption.plans;
let result= animals.filter(animal=>handles.includes(animal.handle));
}

Merge an item attribute of one array into items of another array

I have a few questions in regards to what would be the best approach to do the following:
Call two different API:
axios.get(contents);
axios.get(favorites);
Response will Look like this:
contents: [
{
id: 1,
value: someValue
},
{
id: 2,
value: someValue
}
];
favorites: [
{
id: 1,
contentId: 2
}
];
What would be the best approach to loop through each favorite and add an element to the contens array such as isFavorite: true when the contentId matches the id. It should look as follows:
contents: [
{
id: 1,
value: someValue
{,
{
id: 2,
value: someValue
isFavorite: true
{
];
What would be the best place to do this and is there any ES6 syntax that can easily do this? I currently have the two actions separate, one that gets the contents and one that gets the favorites, I could possibly merge those or combine them at the reducer.
Any suggestions?
You can use a Set to collect all contentId values from favorites and then iterate through your contents array. This has better time complexity than using some on an array because calling .has() on a Set is O(1):
let contents = [{
id: 1,
value: 'someValue1'
},
{
id: 2,
value: 'someValue2'
},
{
id: 3,
value: 'someValue'
}
];
let favorites = [{
id: 1,
contentId: 2
},
{
id: 2,
contentId: 3
}
];
let favoriteContents = new Set(favorites.map(f => f.contentId));
contents.forEach(c => {
if (favoriteContents.has(c.id)) c.isFavorite = true;
});
console.log(contents);
const newContents = contents.map((content) => {
const foundFavorite = favorites.find((favorite) => favorite.contentId === content.id)
if (foundFavorite) {
return {
...content,
isFavorite: true,
}
}
return content
});
You firstly need to have the promises from your API calls, and when both of them are complete you can then carry out the merge of the results.
const contentsApi = () => Promise.resolve([
{
id: 1,
value: 'foo'
},
{
id: 2,
value: 'bar'
}
])
const favouritesApi = () => Promise.resolve([
{
id: 1,
contentId: 2
}
])
let contents;
let favourites;
const contentsApiCall = contentsApi().then(res => {
contents = res;
})
const favouritesApiCall = favouritesApi().then(res => {
favourites = res;
})
Promise.all([contentsApiCall, favouritesApiCall]).then(() => {
const merged = contents.map(content => {
if(favourites.some(favourite => favourite.contentId === content.id)){
return {
...content,
isFavourite: true
}
} else {
return content;
}
})
console.log(merged)
// do whatever you need to do with your result, either return it if you want to chain promises, or set it in a variable, etc.
})

Create object with new key and assign same array

Do you have an optimised solution for the following.
let say,
x = { users: [ {id: 1}, {id:2}, {id:3} ] }
I want to make a new key with same values, the output should be,
{ users: { list: [ {id: 1}, {id:2}, {id:3} ], count: 3 }
Using only JS or Underscore with one line code w/o any extra effort.
I know other tricks to do the same, but do we have one line solution for the same ?
Help appreciated...
Thanks.
Create the object, and assign the x array to list. However, count should be a getter, since the list length might change:
const x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
const result = {
users: {
list: x.users,
get count() { return this.list.length; }
}
};
console.log(JSON.stringify(result));
result.users.list.push({ id: 4 });
console.log(JSON.stringify(result));
I'm not sure why this is to be an optimised solution because this is a simple plain-JavaScript problem:
let x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
let result = {
users: {
list: x.users,
count: x.users.length
}
};
console.log(result);
Sure, just define the property as an object
const obj = {
users: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
};
obj.users = { list: obj.users, count: obj.users.length };
console.log(obj);
I recommend focusing on code clarity rather than on line conservation though

Push one object to another inside JSON

So for example I have object like this:
{
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
And now I need to loop over included and push included to data where id is equal.
So after transformation it would have badge in data, for example like this:
{
data: [
{
id: "13",
name: "id13",
included: {
id: "13",
badge: true
},
},
{
id: "21",
name: "id21",
included: {
id: "21",
badge: false
}
}
]
}
of course I tried on my own and I've created this code:
for(let i=0; i<includedLength; i++) {
console.log(a.included[i].id);
for(n=0; n<dataLength; n++) {
console.log(a.data[n]);
if(a.icluded[i].id === a.data[i].id) {
console.log('We have match!!!');
}
}
}
but it doesn't work I have an error in console
Uncaught TypeError: Cannot read property '0' of undefined
This is demo of my code.
All solutions here have gone in the same path as you had, which is not efficient. So I am posting my solution, which is more efficient than the other solutions so far. Read the code comments to understand the optimizations done.
// Convert data array into a map (This is a O(n) operation)
// This will give O(1) performance when adding items.
let dataMap = a.data.reduce((map, item) => {
map[item.id] = item;
return map;
}, {});
// Now we map items from included array into the dataMap object
// This operation is O(n). In other solutions, this step is O(n^2)
a.included.forEach(item => {
dataMap[item.id].included = item;
});
// Now we map through the original data array (to maintain the original order)
// This is also O(n)
let finalResult = {
data: a.data.map(({id}) => {
return dataMap[id];
})
};
console.log(JSON.stringify(finalResult))
Here is my solution, this will provide the required output!
It constains the same standard for loops.
Some points I would like to highlight are,
The id in included property is string, so you can use the + operator to convert it to number.
The Object.assign() method is used so that we create a new copy of the corresponding object. Read more here
var data = {
data: [{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
var output = {
data: data.data
};
for (var q of data.included) {
for (var j of output.data) {
if (+q.id === j.id) {
j['included'] = Object.assign({}, j);;
}
}
}
console.log(output);
.as-console {
height: 100%;
}
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
It seems like a waste of space to push the whole "included" element into the first array when a match is found (you really need that extra id element in there?) - so this just makes output like
[{id: 1, name: 'name', badge: true},{...}]
If no matching badge element is found, it sets badge to false.
var notJSON = {
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
};
var badged = notJSON.data.map(function (el, i) {
el.badge = notJSON.included.find(function (inc) {
return inc.id == el.id;
}).badge || false;
return el;
});
console.log(badged);
Its not a JSON, its an Object. A valid json consists of both its key and value as string. What you are trying to do is manipulate an object. The following code should help in getting the desired output.
const obj ={
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
for (var i=0; i<obj.data.length;i++){
for(var j=0; j< obj.included.length;j++){
if(obj.data[i].id == obj.included[j].id){
obj.data[i].included={
id: obj.included[j].id,
badge: obj.included[j].badge
}
}
}
}
delete obj.included
console.log(obj)
What I am doing her is:
Checking if id of obj.data is equal to that of obj.included
If they are equal add a new key called "included" in obj[data]
When the loop is over delete the "included" key from obj as its not required anymore.
var obj = {
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
};
obj.included.forEach((item) => {
obj.data.forEach(item1 => {
if(item.id == item1.id){
item1.included = item;
}
});
});
delete obj.included;

how to split array of objects into multiple array of objects by subvalue

I need to split an Array by its objects subvalue (type).
Let's assume I have following array:
[
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
]
and I want to split the object by information.type so my final result looks like:
[
{
type:"employee",
persons:
[
{id:1,name:"John",information: { ... }},
{id:2,name:"Charles",information: { ... }
]
},
{
type:"ceo",
persons:
[
{id:3,name:"Emma",information: { ... }}
]
},
{
type:"customer",
persons:
[
{id:4,name:"Jane",information: { ... }}
]
},
]
Underscore is available at my Project. Any other helper library could be included.
Of course I could loop through the array and implement my own logic, but i was looking for cleaner solution.
This returns exactly what you want:
_.pairs(_.groupBy(originalArray, v => v.information.type)).map(p => ({type: p[0], persons: p[1]}))
A solution in plain Javascript with a temporary object for the groups.
var array = [{ id: 1, name: "John", information: { type: "employee" } }, { id: 2, name: "Charles", information: { type: "employee" } }, { id: 3, name: "Emma", information: { type: "ceo" } }, { id: 4, name: "Jane", information: { type: "customer" } }],
result = [];
array.forEach(function (a) {
var type = a.information.type;
if (!this[type]) {
this[type] = { type: type, persons: [] };
result.push(this[type]);
}
this[type].persons.push({ id: a.id, name: a.name });
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
You could use the groupBy function of underscore.js:
var empList = [
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
];
_.groupBy(empList, function(emp){ return emp.information.type; });

Categories