Create object with new key and assign same array - javascript

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

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

Nested Reduce with values in JS

Good morning, after an array.map I have an array containing the same assignments with some nested ratings:
const assignments = [
{
name: "assignmentOne",
difficultyRating: 1,
funRating: 2
},
{
name: "assignmentOne",
difficultyRating: 3,
funRating: 4
},
{
name: "assignmentOne",
difficultyRating: 5,
funRating: 1
}
]
Now I would like to get the total difficulty/fun rating, which would look like one of the following:
//Both the difficulty and fun rating in the same record
const assignmentsTotal = [
{
name: "assignmentOne",
totalDifficultyRating: 9,
totalFunRating: 7
}
]
//Difficulty and fun rating as separate records
const assignmentsDifficultyTotal = [
{
name: "assignmentOne",
totalDifficultyRating: 9
}
]
const assignmentsFunTotal = [
{
name: "assignmentOne",
totalFunRating: 7
}
]
I'm pretty confident the best way to do this is using the reduce method.
After some digging around the only thing that came close to what I want to achieve is the following article, yet I'm not able to get this to work properly. Is there a good way to do this from the starting point above, or would it be better to create separate arrays using array.map and after use the reduce method?
If you are looking for same 'name' objects in array, below should be ok:
const reducer = assignments.reduce((total, current) => {
return { name: current.name, difficultyRating : total.difficultyRating + current.difficultyRating, funRating : total.funRating + current.funRating } });
if you want to group objects by name, then look at lodash groupby function. In general, lodash is very handy in all array/obj functionalities.
const assignments = [{
name: "assignmentOne",
difficultyRating: 1,
funRating: 2
},
{
name: "assignmentOne",
difficultyRating: 3,
funRating: 4
},
{
name: "assignmentOne",
difficultyRating: 5,
funRating: 1
},
{
name: "assignmentTwo",
difficultyRating: 5,
funRating: 3
},
{
name: "assignmentTwo",
difficultyRating: 5,
funRating: 1
}
];
// if you want the totals as an array:
const assignmentsTotalArray = assignments.reduce((totalArr, item) => {
// check whether the assignment is already in the array
const assignmentIndex = totalArr.findIndex(elem => elem.name === item.name);
// if the assignment is not in the array, add it and initialize the totals
// otherwise update the totals
if (assignmentIndex === -1) {
totalArr.push({
name: item.name,
totalDifficultyRating: item.difficultyRating,
totalFunRating: item.funRating
});
} else {
totalArr[assignmentIndex].totalDifficultyRating += item.difficultyRating;
totalArr[assignmentIndex].totalFunRating += item.funRating;
}
return totalArr;
}, []);
console.log('### assignmentsTotalArray:');
console.log(assignmentsTotalArray);
// if you want the totals as an object:
const assignmentsTotalObject = assignments.reduce((totalObj, item) => {
// if the output object already contains the assignment, sum the ratings
// otherwise create a new key for the assignment and initialize the ratings
if (totalObj[item.name]) {
totalObj[item.name].totalDifficultyRating += item.difficultyRating;
totalObj[item.name].totalFunRating += item.funRating;
} else {
totalObj[item.name] = {
totalDifficultyRating: item.difficultyRating,
totalFunRating: item.funRating
};
}
return totalObj;
}, {});
console.log('### assignmentsTotalObject:')
console.log(assignmentsTotalObject);

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

How can I concat an array to an array that is inside an array of objects?

I am trying to concat an array to an array (productsCategories) inside an array of objects.
So, here's what the productCategories array looks like:
[
{
id: 123,
items: [ { Obj1 }, { Obj2 } ]
},
{
id:456,
items: [ { Obj1 }, { Obj2 } ]
}
]
I have some new array, like [ { Obj3 }, { Obj4 } ] that I want to concat to the productCategories for the object where id = 123.
So to do this,
I've first used lodash's find to find the correct object to update and used concat to join the two arrays:
let nextItems:any = find(productCategories, { id: payload.id });
nextItems = assign({}, nextItems, { items: nextItems.items.concat(payload.items)});
So, nextItems.items has the concatenated items array.
However, I am having trouble now adding this to productCategories array. I need to find the object where id is the same as nextItems.id and then set productCategories.items equal to nextItems.items.
What is the correct way to do this?
Find the index of the object that matches the nextItems.id in the productCategories and assign the new concatenated array to it. You can use the lodash findIndex() method to find the index of the object that matches the id.
var index = _findIndex(productCategories, { id: nextItems.id });
productCategories[index].items = nextItems.items;
You can use plain JavaScript just as well. With ES6 spread syntax it can look like this:
productCategories.filter(x => x.id == payload.id)
.forEach(x => x.items.push(...payload.items));
Here is a snippet with sample data:
// Sample data
var productCategories = [{
id: 123,
items: [ { a: 1 }, { a: 2 } ]
}, {
id: 456,
items: [ { b: 1 }, { b: 2 } ]
}];
var payload = {
id: 123,
items: [ { c: 1 }, { c: 2 } ]
};
// Update with payload
productCategories.filter(x => x.id == payload.id)
.forEach(x => x.items.push(...payload.items));
// Show results
console.log(productCategories);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Transform javascript array with objects with lodash

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

Categories