This question already has answers here:
Looping through array and removing items, without breaking for loop
(17 answers)
Closed 2 years ago.
I am trying to delete the all the objects with _id "bennyRawMaterial" from below details array nested in the object:
let recipeBasicRecipes = [{
_id:'12345',
name:'Macaron Shell',
details:[
{
name: 'Sugar',
_id: 'bennyRawMaterial',
type: 'solid',
}
,
{
name: 'Egg white',
_id: '5fef680ca43301322a3224e5',
type: 'solid'
}]
},
{
_id:'14512345',
name:'Macaron Shell',
details:[{
name: 'Castiors gar',
_id: 'bennyRawMaterial',
type: 'solid'
},
{
name: 'oil',
_id: 'bennyRawMaterial',
type: 'solid',
}
, {
name: 'food',
_id: 'bennyRawMaterial',
type: 'solid',
}]
}]
I am using following code to remove the objects, but it skips few of the objects.Please help with the implementation
recipeBasicRecipes.forEach(br => {
br.details.forEach((rm, index) => {
if (rm._id === 'bennyRawMaterial') {
br.details.splice(index, 1);
} else {
return true;
}
});
maintain a global ids array,
and go through the details array of each object
check the id exist in global ids array
let recipeBasicRecipes = [{
_id: '12345',
name: 'Macaron Shell',
details: [{
name: 'Sugar',
_id: 'bennyRawMaterial',
type: 'solid',
},
{
name: 'Egg white',
_id: '5fef680ca43301322a3224e5',
type: 'solid'
}
]
},
{
_id: '14512345',
name: 'Macaron Shell',
details: [{
name: 'Castiors gar',
_id: 'bennyRawMaterial',
type: 'solid'
},
{
name: 'oil',
_id: 'bennyRawMaterial',
type: 'solid',
}, {
name: 'food',
_id: 'bennyRawMaterial',
type: 'solid',
}
]
}
]
var uniqueIds = []; //global ids array
recipeBasicRecipes.forEach(el => {
let details = [];//unique details array
el.details.forEach((dt, i) => {
let id = dt._id;
if (!uniqueIds.includes(id)){ //check id exists in global ids array
uniqueIds.push(id);
details.push(dt); //copy unique details
}
});
el.details = details; //update details with unique details
});
console.log(recipeBasicRecipes)
Related
let arr = [
{
name: 'siggaret',
type: 'tobbako'
},
{
name: 'fanta',
type: 'liquid'
},
{
name: 'potato',
type: 'vegetables'
},
{
name: 'tvorog',
type: 'milk'
},
{
name: 'steak',
type: 'meat'
},
{
name: 'kent',
type: 'tobbako'
},
{
name: 'cola',
type: 'liquid'
},
{
name: 'potato',
type: 'vegetables'
},
{
name: 'tvorog',
type: 'milk'
},
{
name: 'steak',
type: 'meat'
},
{
name: 'sheep',
type: 'meat'
}
]
let categories = [
{
type: 'vegetables',
arr: [],
count: 0
},
{
type: 'tobbako',
arr: [],
count: 0
},
{
type: 'liquid',
arr: [],
count: 0
},
{
type: 'other',
arr: [],
count: 0
}
]
/*
*/
for (let item of arr) {
for (let category of categories) {
if(item.type === category.type){
category.arr.push(item.name)
category.count++
} else {
category.arr.push(item.name)
category.count++
}
}
}
console.log(categories)
There items is not added in others ? What is the problem ?
I try to sort all items by category.
There nested loop is not worked well but I try with for of and there are some problems with sorted.
When I try to sort by item.name and category.type all items' name is added to the all category.
I have two arrays and I need to find the difference between the two and display them in an unordered list.
I can loop through the master array for a single match but I have no clue how to loop through the master list for more than one key value and do it efficiently.
Below is an excerpt of key values from each array:
Instead of looping through two arrays, you could convert categories into an object, where category type is a key, so you can just use type as key:
let arr = [
{
name: 'siggaret',
type: 'tobbako'
},
{
name: 'fanta',
type: 'liquid'
},
{
name: 'potato',
type: 'vegetables'
},
{
name: 'tvorog',
type: 'milk'
},
{
name: 'steak',
type: 'meat'
},
{
name: 'kent',
type: 'tobbako'
},
{
name: 'cola',
type: 'liquid'
},
{
name: 'potato',
type: 'vegetables'
},
{
name: 'tvorog',
type: 'milk'
},
{
name: 'steak',
type: 'meat'
},
{
name: 'sheep',
type: 'meat'
}
]
let categories = {
'vegetables': {
arr: [],
count: 0
},
'tobbako': {
arr: [],
count: 0
},
'liquid': {
arr: [],
count: 0
},
'other': {
arr: [],
count: 0
}
}
for (let item of arr) {
//get category object or fallback to "other"
const category = categories[item.type] || categories.other;
category.arr.push(item.name)
category.count++
}
console.log(categories)
// now we can get an array sorted by count of our categories name
const sortedByCount = Object.keys(categories) //get array of types
.sort((a,b) => categories[b].count - categories[a].count) // sort by count
.map(type => type + " = " + categories[type].count); // append count
console.log("categories sorted by count", sortedByCount);
//or convert it into array w/sorted names
const categoriesArray = Object.keys(categories) //get array of types
.map(type => //convert types into objects
{
const category = Object.assign({}, categories[type]); //clone object
// const category = categories[type]; //don't clone object
category.type = type;
category.arr = [...category.arr].sort(); //clone array and sort it
// category.arr.sort(); //sort array of names without clonning
return category;
})
.sort((a,b) => b.count - a.count); //sort by count
console.log("categories as array sorted by count w / sorted names", categoriesArray);
.as-console-wrapper{top:0;max-height:unset!important;overflow:auto!important;}
Good start.
The problem is in your else statement. What happens is that:
else {
category.arr.push(item.name)
category.count++
}
triggers too often. Every time a category does not match it is pushed.
How to fix: You need handle the "pushing to "others" part" outside of that loop.
In your code, both branches of the if () { ... } else { ... } run the same code. You should remove the else { ... } branch.
Like this:
for (let item of arr) {
for (let category of categories) {
if (item.type === category.type){
category.arr.push(item.name);
category.count++;
}
}
}
The problem is in the else statement, basically, you don't need the else statement cause if you see the code they both are identical, in your case you are adding items to categories, So remove the else statement:
for (let item of arr){
for (let category of categories) {
if(item.type === category.type){
category.arr.push(item.name);
category.count++;
}
}
}
You can use a combination of Object.values() and Array#reduce() methods as follows, and then use sort with map to sort the arrays:
const
arr = [ { name: 'siggaret', type: 'tobbako' }, { name: 'fanta', type: 'liquid' }, { name: 'potato', type: 'vegetables' }, { name: 'tvorog', type: 'milk' }, { name: 'steak', type: 'meat' }, { name: 'kent', type: 'tobbako' }, { name: 'cola', type: 'liquid' }, { name: 'potato', type: 'vegetables' }, { name: 'tvorog', type: 'milk' }, { name: 'steak', type: 'meat' }, { name: 'sheep', type: 'meat' } ],
categories = Object.values(
arr.reduce(
(acc,{name,type}) =>
({
...acc,[type]:{
type,
arr:[...(acc[type] && acc[type]?.arr || []),name],
count:(acc[type] && acc[type].count || 0) + 1
}
}), {}
)
)
.sort((a,b) => a.type.localeCompare(b.type))
.map(({type,arr,count}) => ({type,arr:arr.sort((a,b) => a.localeCompare(b)),count}));
console.log( categories );
I have an object looking like this
const item = {
id: 123,
type: 'book',
sections: [{
type: 'section',
id: '456',
index: 1,
lessons: [{
type: 'lesson',
id: 789,
index: 1
},
{
type: 'lesson',
id: 999,
index: 2
}
]
}, {
type: 'section',
index: 2,
id: 321,
lessons: [{
type: 'lesson',
id: 444,
index: 1
},
{
type: 'lesson',
id: 555,
index: 2
}
]
}]
}
It should be assumed that there are more objects in sections and lessons array. I want to create a new object like this
result = [{
section: 456,
lessons: [789, 999]
}, {
section: 321,
lessons: [444, 555]
}]
I tried this loop but this just pushes indexes and not lesson's ids
let obj = {};
let sectionWithLessons = [];
let lessons = []
for (const i in item.sections) {
obj = {
sectionId: item.sections[i].id,
lessonIds: item.sections[i].lessons.map((lesson) => {
return lessons.push(lesson.id)
}),
};
sectionWithLessons.push(obj);
}
console.log(sectionWithLessons);
How can i do this correctly and preferably with good performance in consideration?
I believe the best/shortest thing is to use the map function, like:
const result2 = item.sections.map(({id, lessons}) => ({
id,
lessons: lessons.map(({id: lessionId}) => lessionId)
}))
I would suggest using Array.map() to convert the item sections to the desired result.
We'd convert each section into an object with a section value and lessons array.
To create the lessons array, we again use Array.map() to map each lesson to a lesson id.
const item = { id: 123, type: 'book', sections: [{ type: 'section', id: '456', index: 1, lessons: [{ type: 'lesson', id: 789, index: 1 }, { type: 'lesson', id: 999, index: 2 } ] }, { type: 'section', index: 2, id: 321, lessons: [{ type: 'lesson', id: 444, index: 1 }, { type: 'lesson', id: 555, index: 2 } ] }] }
const result = item.sections.map(({ id, lessons }) => {
return ({ section: +id, lessons: lessons.map(({ id }) => id) })
});
console.log('Result:', result);
.as-console-wrapper { max-height: 100% !important; }
This question already has answers here:
Add property to an array of objects
(7 answers)
Closed 10 months ago.
I am making an API call in React and I would like to add an additional price property to the results, with each property having a different value for each result.
For example...
API response:
{
id: 456
name: "capuccino",
type: "coffee",
},
{
id: 457
name: "latte",
type: "coffee",
},
{
id: 458
name: "americano",
type: "coffee",
}
Is there a way to dynamically add an additional price property, each with a different value to get the result below?
{
id: 456
name: "capuccino",
type: "coffee",
**price: 5.99**
},
{
id: 457
name: "latte",
type: "coffee",
**price: 10.00**
},
{
id: 458
name: "americano",
type: "coffee",
**price: 8.90**
}
You could create a prices array and use map() and the index to map the price to the correct object.
const response = [{
id: 456,
name: "capuccino",
type: "coffee",
},
{
id: 457,
name: "latte",
type: "coffee",
},
{
id: 458,
name: "americano",
type: "coffee",
}
];
const prices = [2, 5, 27];
const result = response.map((o, i) => ({ ...o, price: prices[i] }));
console.log(result);
I have two arrays of objects
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
]
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
]
I need to write a function to output an array with objects with the same name being merged into one.
const c = [
{ name: 'apple', type: 'fruit', amount: 4 },
{ name: 'berry', type: 'fruit', amount: 5 },
{ name: 'grape', type: 'fruit', amount: 3 },
{ name: 'broccoli', type: 'vegetable', amount: 7 },
{ name: 'cabbage', type: 'vegetable', amount: 0 },
{ name: 'avocado', type: undefined, amount: 8 },
]
As you can see here, objects that share the same name are merged into one object with a few exceptions:
if type field is missing, we would need to add it and make it undefined
if amount field is missing, we need to add it and make it 0
Here is my attempt:
function fillMissingFields(object) {
console.log('object', object)
let newObject = { ...object }
if (object.type === undefined) {
newObject = { ...object, type: undefined }
}
if (object.amount === undefined) {
newObject = { ...newObject, amount: 0 }
}
return newObject
}
function join(a, b) {
const results = []
for (const aItem of a) {
const bItems = b.filter((item) => item.name === aItem.name)
let newObject
if (bItems.length) {
for (const bItem of bItems) {
newObject = { ...newObject, ...bItem }
}
newObject = fillMissingFields({ ...newObject, ...aItem })
} else {
newObject = fillMissingFields(aItem)
}
results.push(newObject)
}
return results
}
Besides the fact that it has a really bad time complexity O(n^2). It actually has a bug where if an object only appears in b array, that object will be omitted entirely from the new array.
Can anyone try to help me come up with a more robust and efficient algorithm to tackle this problem?
Make a collection whose keys are the names, whose values are the combined objects, which starts out with an undefined type and an amount of 0. Iterate through both arrays, assigning the property values as needed, then at the end, take the collection's values:
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
];
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
];
const objsByName = new Map();
const getObj = (name) => {
if (!objsByName.has(name)) {
objsByName.set(name, { name, type: undefined, amount: 0 });
}
return objsByName.get(name);
};
for (const { name, type } of a) {
getObj(name).type = type;
}
for (const { name, amount } of b) {
getObj(name).amount = amount;
}
console.log([...objsByName.values()]);
This following approach will work for dynamic objects which have different keys other than name, type or amount. Although there is one problem with that approach we can not define default values for each variable(like for amount default will be 0 or for other key default will be undefined). In my approach if any key is missing in any of the object the default value will be undefined.
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
]
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
]
const c = [...a, ...b];
const s = new Set();
let d = c.reduce((acc, curr) => {
const index = acc.findIndex(item => item.name === curr.name);
if(index > -1) {
acc[index] = {...acc[index], ...curr};
} else {
acc.push(curr);
}
Object.keys(curr).forEach(key => s.add(key));
return acc;
}, []);
let res = d.map(item => {
let keyInObj = Object.keys(item);
Array.from(s).forEach(actualKey => {
if(!keyInObj.includes(actualKey)) {
item[actualKey] = undefined;
}
});
return item;
})
console.log(res);
Please find below code snippet creating a unique array of objects out of two arrays. If value is not present in amount or type, we are writing 0 or undefined respectively.
const a = [
{ name: "apple", type: "fruit" },
{ name: "berry", type: "fruit" },
{ name: "grape", type: "fruit" },
{ name: "broccoli", type: "vegetable" },
{ name: "cabbage", type: "vegetable" },
];
const b = [
{ name: "apple", amount: 4 },
{ name: "berry", amount: 5 },
{ name: "grape", amount: 3 },
{ name: "broccoli", amount: 7 },
{ name: "avocado", amount: 8 },
];
let d = [...a, ...b];
const c = [];
d.map((o) => {
const uniqIndex = c.findIndex((item) => item.name === o.name);
uniqIndex === -1
? c.push({
amount: 0,
type: undefined,
...o,
})
: (c[uniqIndex] = {
...c[uniqIndex],
...o,
});
});
console.log(c);
I have 2 arrays in JavaScript. One of which needs filtering based on a property from the other one.
I have a movies list such as this:
[
{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
}
},
...
]
I then have another array which contains movies that a user has watched but they are within a nested object within the array as a relationship following the JSON API spec.
[
{
id: '1',
type: 'moviesWatched',
attributes: {
comment: 'Excellent film, would recommend to anyone who loves a action film!'
},
relationships: {
movie: {
data: {
type: 'movies',
id: 2
}
}
}
}
]
What I need to achieve is I need to cross reference the id from watchList[].relationships.movie.data.id with the first array. Obviously, this list is a lot longer in production but what I am hoping to achieve is to have a full list of movies which the use has watched with the name of the movie based off 2 arrays formatted like this to save me having to store needless data inside of the database.
The outcome of the array would be something along the lines of...
[
{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
},
meta: {
watched: false
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
},
meta: {
watched: true
}
}
]
Here is what I have currently, which is working but I don't know if there is a better way to go about it...
movies.map((movie) => {
const watched = watchedMovies.find((searchable) => {
return searchable.relationships.movie.data.id === searchable.id;
});
movie.meta.watched = watched || false;
return movie;
});
Since you said the list can be long, you can extract the "watched" movie ids to a Set and then set the meta.watched property of a movie based on whether or not its id is in the set:
const movies = [{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
}
}
];
const watched = [{
id: '1',
type: 'moviesWatched',
attributes: {
comment: 'Excellent film, would recommend to anyone who loves a action film!'
},
relationships: {
movie: {
data: {
type: 'movies',
id: 2
}
}
}
}];
const watchedSet = new Set(watched.map(m => m.relationships.movie.data.id));
movies.forEach(movie => movie.meta = {watched: watchedSet.has(movie.id)});
console.log(movies);