Javascript .find() not working for nested object array [duplicate] - javascript

This question already has answers here:
Curly Brackets in Arrow Functions
(3 answers)
Closed 1 year ago.
I have two nested object arrays, one is an array describing a school (id, name, tests it conducts along with their counts), and another is an array of school teachers corresponding to the schools in the first array. Here is some sample data for both arrays:
import { ObjectId } from 'mongodb'
monthlySchoolTests = [{
schoolId: ObjectId('804d8527f390ghz26e3426j6'),
schoolName: "School1"
schoolTests: [
{ testName: "Chemistry", count: 15 },
{ testName: "Music", count: 8 }
]
},
{
schoolId: ObjectId('804ef074384b3d43f125ghs5'),
schoolName: "School2"
schoolTests: [
{ testName: "Math", count: 3 },
{ testName: "Physics", count: 12 },
{ testName: "Biology", count: 10 }
]
}
]
schoolTeachers = [{
schoolId: ObjectId('804ef074384b3d43f125ghs5')
schoolName: "School2"
teachers: [
{ name: "Michael", email: "michael#school2.edu" },
{ name: "Jane", count: "jane#school2.edu" },
{ name: "Lukas", count: "lukas#school2.edu" }
]
},
{
schoolId: ObjectId('804d8527f390ghz26e3426j6')
schoolName: "School1"
teachers: [
{ name: "Cleo", email: "cleo#school1.edu" },
{ name: "Celine", count: "celine#school1.edu" },
{ name: "Ike", count: "ike#school1.edu" }
]
}
]
I obtained the second array by passing as an argument an array of schoolIds that I got from the query results for the first array, so I know that both arrays have about the same number of objects (assuming queries don't return null values). I am trying to link the test information in the first array with the school teacher in the second array but the .find() method is not working for me. Here is my code:
monthlySchoolTests.map(testObj => {
const selectedSchoolTeachers = schoolTeachers.find(teacherObj => {
String(testObj.schoolId) === String(teacherObj.schoolId)
})
console.log(selectedSchoolTeachers)
})
For some reason, I only get undefined even though I have tested this by mapping through each object in the first array and asserting that there is a match for every schoolId in the second array. (console.log yields true).
Sorry for any errors as this is my first time posting. Would appreciate any help!

Well, you could just quick-generate a combined array that had everything you need in it:
let combined = monthlySchoolTests.map(e => ({...e, teachers: schoolTeachers.filter(t => t.schoolId === e.schoolId)[0].teachers}))
const ObjectId = (v) => v; // for testing
let monthlySchoolTests = [{
schoolId: ObjectId('804d8527f390ghz26e3426j6'),
schoolName: "School1",
schoolTests: [
{ testName: "Chemistry", count: 15 },
{ testName: "Music", count: 8 }
]
},
{
schoolId: ObjectId('804ef074384b3d43f125ghs5'),
schoolName: "School2",
schoolTests: [
{ testName: "Math", count: 3 },
{ testName: "Physics", count: 12 },
{ testName: "Biology", count: 10 }
]
}
]
let schoolTeachers = [{
schoolId: ObjectId('804ef074384b3d43f125ghs5'),
schoolName: "School2",
teachers: [
{ name: "Michael", email: "michael#school2.edu" },
{ name: "Jane", count: "jane#school2.edu" },
{ name: "Lukas", count: "lukas#school2.edu" }
]
},
{
schoolId: ObjectId('804d8527f390ghz26e3426j6'),
schoolName: "School1",
teachers: [
{ name: "Cleo", email: "cleo#school1.edu" },
{ name: "Celine", count: "celine#school1.edu" },
{ name: "Ike", count: "ike#school1.edu" }
]
}
]
let combined = monthlySchoolTests.map(e => ({...e, teachers: schoolTeachers.filter(t => t.schoolId === e.schoolId)[0].teachers}))
console.log(combined)

Related

Compare two arrays of objects and merge some fields

I'm working with Angular and RxJs and I have two arrays of objects. I need to change one specific field of the first array, if the second one has the field with the same value (all of the four fields has different names). I did it with nested loops, but I need to find a better solution, my code is down below
My solution is working, but it's not the best, because arrays can be really large - so the code will work slow. If there's 1000 items in each array, it will be 1000000 iterations - that's why I need to find a better solution. I got advice to use multiple consecutive loops, but I don't really get how to use it here
this.api
.getFirstArray()
.pipe(
mergeMap((firstArray) =>
this._secondApi.getSecondArray().pipe(
map((secondArray) => {
for (const item2 of secondArray) {
for (const item1 of firstArray) {
if (item1.someField === item2.otherField)
item1.someOtherField = item2.anotherField;
}
}
return firstArray;
}),
),
),
)
.subscribe((value) => {
this.gridApi?.setRowData(value);
});
So for example my data is
firstArray: [
{ id: 445; name: 'test' },
{ id: 4355; name: 'test1' },
{ id: 234_234; name: 'test2' },
];
secondArray: [
{ firstName: 'test3'; newId: 445 },
{ firstName: 'test5'; newId: 2 },
{ firstName: 'test6'; newId: 234_234 },
];
And the result should be
result: [{ id: 445; name: 'test3' }, { id: 4355; name: 'test1' }, { id: 234_234; name: 'test6' }];
Note: the ids of the first array objects may be repeated - all of the objects names need to be updated
here is the working example of your problem, may be it will help you.
let firstArray = [
{ id: 445, name: 'test' },
{ id: 4355, name: 'test1' },
{ id: '234_234', name: 'test2' },
];
let secondArray = [
{ firstName: 'test3', newId: 445 },
{ firstName: 'test5', newId: 2 },
{ firstName: 'test6', newId: '234_234' },
];
secondArray.forEach(sec => {
let see = firstArray.findIndex(first => first.id === sec.newId);
if (see > -1) {
firstArray[see].name = sec.firstName
}
})
console.log(firstArray)
You still end up with O(N²) complexity (there are two nested loops that he wants to avoid).
Instead, You can use map
const firstArray = [
{ id: 445, name: 'test' },
{ id: 4355, name: 'test1' },
{ id: '234_234', name: 'test2' },
];
const secondArray = [
{ firstName: 'test3', newId: 445 },
{ firstName: 'test5', newId: 2 },
{ firstName: 'test6', newId: '234_234' },
];
const secondMap = new Map();
secondArray.forEach((item) => {
secondMap.set(item.newId, item.firstName);
});
for (const item of firstArray) {
if (secondMap.has(item.id)) {
item.name = secondMap.get(item.id);
}
}
console.log(firstArray)

Looping Through and Displaying Deeply Nested Objects/JSON with React

I'm making an API request and running the data through groupBy and sortBy to get a slightly more structured object. Then I save it to the state.
useEffect(() => {
const fetchTeamData = async () => {
const result = await axios(
`https://example.com/api-call`,
).then((result) => {
let teams = _.groupBy(_.sortBy(result.data.results, "season"), "player_profile.team");
setTeamInfo(teams);
});
};
fetchTeamAffinityData();
}, []);
This gives me an object similar to:
{
Angels: [
0: {
player_profile:{
name: "John Doe",
number: "10"
},
season: 1
},
1: {
player_profile:{
name: "Mike Trout",
number: "21"
},
season: 2
}
],
Diamondbacks: [
0: {
player_profile:{
name: "Randy Johnson",
number: "51"
},
season: 1
},
1: {
player_profile:{
name: "Brandon Webb",
number: "16"
},
season: 2
}
],
}
This is the output I'm looking for:
Team: Angels
Player 1: John Doe
Player 1 Number: 10
Player 2: Mike Trout
Player 2 Number: 21
Team: Diamondbacks
Player 1: Randy Johnson
Player 1 Number: 51
Player 2: Bradon Webb
Player 2 Number: 17
Here is what I've tried, which gives me a loop of the team names. But since I'm mapping the object keys, I lose the actual data for the nested object.
{Object.keys(teamAffinityInfo?? "").map((team, index) => (
Team: {team}
))}
How can I work with deeply nested objects in a sensible way?
Based on your approach, you could store Object.keys() and then iterate on those keys to implement your logic.
JS
const teams = Object.keys(object1)
teams.forEach(team => {
const playerProfiles = Object1[team]
playerProfiles.forEach(p => {
// Your logic here
})
})
Better approach would be to use Object.entries() as mentioned by Ogod in the comment.
JS
const object1 = {
Angels: [
{
player_profile:{
name: "John Doe",
number: "10"
},
season: 1
},
{
player_profile:{
name: "Mike Trout",
number: "21"
},
season: 2
}
],
Diamondbacks: [
{
player_profile:{
name: "Randy Johnson",
number: "51"
},
season: 1
},
{
player_profile:{
name: "Brandon Webb",
number: "16"
},
season: 2
}
],
}
for (const [key, value] of Object.entries(object1)) {
const players = value
console.log("Team", key)
players.forEach((p,i) => {
console.log("Player",i, ":", p.player_profile.name)
console.log("Player",i, "Number :", p.player_profile.number)
})
}
Note: I slightly changed the structure of object, by removing the index in the profiles array.
Here is an iterative solution using object-scan
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.1.2/lib/index.min.js';
const myData = { Angels: [ { player_profile: { name: "John Doe", number: "10" }, season: 1 }, { player_profile: { name: "Mike Trout", number: "21" }, season: 2 } ], Diamondbacks: [ { player_profile: { name: "Randy Johnson", number: "51" }, season: 1 }, { player_profile: { name: "Brandon Webb", number: "16" }, season: 2 } ], };
const extract = (data) => {
const logic = {
'*': ({ key }) => `Team: ${key[0]}`,
'*[*].player_profile.name': ({ key, value }) => `Player ${key[1] + 1}: ${value}`,
'*[*].player_profile.number': ({ key, value }) => `Player ${key[1] + 1} Number: ${value}`
}
return objectScan(Object.keys(logic), {
breakFn: ({ context, matchedBy, key, value }) => {
context.push(...matchedBy.map((m) => logic[m]({ key, value })))
},
reverse: false
})(data, []);
}
console.log(extract(myData).join('\n'));
/* =>
Team: Angels
Player 1: John Doe
Player 1 Number: 10
Player 2: Mike Trout
Player 2 Number: 21
Team: Diamondbacks
Player 1: Randy Johnson
Player 1 Number: 51
Player 2: Brandon Webb
Player 2 Number: 16
*/
</script>
Disclaimer: I'm the author of object-scan

How to remove a property from array of objects [duplicate]

This question already has answers here:
Remove property for all objects in array
(18 answers)
Closed 1 year ago.
I have an array of objects with name and age property:
[
{ name: "Matthew", age: 23 },
{ name: "James", age: 20 },
{ name: "Daniel", age: 25 },
{ name: "Joshua", age: 22 }
]
I want to remove age property from all of the objects and print in console like
[
{ name: "Matthew" },
{ name: "James" },
{ name: "Daniel" },
{ name: "Joshua" }
]
Iterate over your array and use delete keyword.
let array = [
{ name: "Matthew", age: 23 },
{ name: "James", age: 20 },
{ name: "Daniel", age: 25 },
{ name: "Joshua", age: 22 }
]
array.forEach(function(v){ delete v.age });
console.log(array);
You can use map function to achieve this.
let output = test.map(({name}) => ({name}));
If you want to filter with multiple object you can add after name like {name, salary}
var test =[
{ name: "Matthew", age: 23 },
{ name: "James", age: 20 },
{ name: "Daniel", age: 25 },
{ name: "Joshua", age: 22 }
];
let output = test.map(({name}) => ({name}));
console.log(JSON.stringify(output, null, 2));
const newData = oldData.map(item=>({name:item.name}))

Reduce and sum array of objects (JS) [duplicate]

This question already has answers here:
Sum JavaScript object propertyA values with the same object propertyB in an array of objects
(12 answers)
Group by, and sum, and generate an object for each array in JavaScript
(4 answers)
How to group by and sum an array of objects? [duplicate]
(2 answers)
Group objects by multiple properties in array then sum up their values
(16 answers)
Closed 3 years ago.
My data:
arr: [],
models: [
{ id: 1, name: "samsung", seller_id: 1, count: 56 },
{ id: 1, name: "samsung", seller_id: 2, count: 68 },
{ id: 2, name: "nokia", seller_id: 2, count: 45 },
{ id: 2, name: "nokia", seller_id: 3, count: 49 }
]
Expected Arr:
arr: [
{ id: 1, name: "samsung", count: 124 },
{ id: 2, name: "nokia", count: 94 }
]
It's my code to simplify models by removing repeated id:
this.models.forEach(mdl => {
if (!this.arr.some(obj => obj.id === mdl.id)) {
this.arr.push(mdl);
}
});
But I can't sum counts.
How can I do that?
You can use Array.reduce():
var models = [
{ id: 1, name: "samsung", seller_id: 1, count: 56 },
{ id: 1, name: "samsung", seller_id: 2, count: 68 },
{ id: 2, name: "nokia", seller_id: 2, count: 45 },
{ id: 2, name: "nokia", seller_id: 3, count: 49 }
];
var arr = models.reduce((acc, item) => {
let existItem = acc.find(({id}) => item.id === id);
if(existItem) {
existItem.count += item.count;
} else {
acc.push(item);
}
return acc;
}, []);
console.log(arr);
So, for your code, you can use this.arr and this.models replacing those variables from above which will look something like:
this.arr = this.models.reduce((acc, item) => {
let existItem = acc.find(({id}) => item.id === id);
if(existItem) {
existItem.count += item.count;
} else {
acc.push(item);
}
return acc;
}, []);
You can use Object.values instead of some. Inside the reduce callback create an object with key as id and value from the models. Then use Object.values to create an array of the values
let models = [{
id: 1,
name: "samsung",
seller_id: 1,
count: 56
},
{
id: 1,
name: "samsung",
seller_id: 2,
count: 68
},
{
id: 2,
name: "nokia",
seller_id: 2,
count: 45
},
{
id: 2,
name: "nokia",
seller_id: 3,
count: 49
}
]
let data = models.reduce((acc, curr) => {
if (!acc[curr.id]) {
acc[curr.id] = curr;
} else {
acc[curr.id].count += curr.count
}
return acc;
}, {})
console.log(Object.values(data))

Javascript : Sort the array

i need to sort the array of people according to who has the most interests in common with Sharon. The person with the most interests matching should be on top. getSortedList() function that sorts the lists of people - whoever has the most similar interests with the 'sharon' object (3 matching interests) should be on top
const interestList = [
"gaming",
"driving",
"football",
"fishing",
"painting",
"cooking",
"singing",
"shopping",
"running",
"clubbing"
];
const people = [
{ name: "Ahmad", interests: ["shopping", "painting", "cooking"] },
{ name: "Betty", interests: ["running", "painting", "football"] },
{ name: "Charlie", interests: ["gaming", "football", "painting"] },
{ name: "Diana", interests: ["fishing", "singing", "driving"] },
{ name: "Ethan", interests: ["gaming", "clubbing", "cooking"] },
{ name: "Farhan", interests: ["cooking", "driving", "fishing"] },
{ name: "Gwen", interests: ["singing", "fishing", "gaming"] },
{ name: "Helen", interests: ["football", "clubbing", "shopping"] },
{ name: "Imelda", interests: ["painting", "running", "football"] },
{ name: "Josef", interests: ["shopping", "running", "cooking"] },
{ name: "Khan", interests: ["fishing", "running", "clubbing"] },
{ name: "Lionel", interests: ["gaming", "singing", "driving"] }
];
const sharon = {
name: "Sharon",
interests: ["football", "painting", "gaming"]
};
function getSortedList() {
let output = people.slice();
return person;
}
function printPeople() {
let list = getSortedList();
list.unshift(sharon);
list.forEach(person => {
person.interest1 = person.interests[0];
person.interest2 = person.interests[1];
person.interest3 = person.interests[2];
delete person.interests;
});
console.log("Friend Matching Script Output:");
console.table(list);
console.table(getSortedList());
}
printPeople();
You can try something like this. If you need to modify your sort to beyond just the count, you can tweak the sort method.
let people = [
{ name: "Ahmad", interests: ["shopping", "painting", "cooking"] },
{ name: "Betty", interests: ["running", "painting", "football"] },
{ name: "Charlie", interests: ["gaming", "football", "painting"] },
{ name: "Diana", interests: ["fishing", "singing", "driving"] },
{ name: "Ethan", interests: ["gaming", "clubbing", "cooking"] },
{ name: "Farhan", interests: ["cooking", "driving", "fishing"] },
{ name: "Gwen", interests: ["singing", "fishing", "gaming"] },
{ name: "Helen", interests: ["football", "clubbing", "shopping"] },
{ name: "Imelda", interests: ["painting", "running", "football"] },
{ name: "Josef", interests: ["shopping", "running", "cooking"] },
{ name: "Khan", interests: ["fishing", "running", "clubbing"] },
{ name: "Lionel", interests: ["gaming", "singing", "driving"] }
];
const sharon = {
name: "Sharon",
interests: ["football", "painting", "gaming"]
};
people.forEach((person, index)=> {
let count = 0;
person.interests.forEach(int => {
sharon.interests.forEach(interest => {
if(int === interest) {
count++
people[index]['count'] = count;
}
})
})
})
people.sort((a,b) => {
if(a.count >= b.count) {
return -1;
} else {
return 1;
}
})
Determine count of overlapping interests, then apply sort.
people.map(item => {
let count = 0;
sharon.interests.forEach(interest => {if (item.interests.lastIndexOf(interest) > -1) ++count});
item.counts = count;
return item;
}).sort((a,b) => a.counts < b.counts ? 1 : (a.counts > b.counts ? -1 : 0))
Usually, the easiest way to sort something is when they are in number form. Currently, you only have data in the form of arrays and objects, which is difficult to sort by itself. Instead, as you want to sort by the number of common interests between two people, we can use this number to help us sort your array of objects.
Thus, the first step to solving your problem is to figure out a way to find the number of "overlapping" interests one object has with another. This can be done by using a method such as this:
const get_overlapping = interests =>
sharon.interests.filter(hobby => interests.includes(hobby)).length;
The above method will loop through sharon's interests (using .filter()), and only keep those which appear (.includes()) in the passed in interests array. As .filter returns an array of overlapping interests with sharon's, we can then simply get the .length of this array to find the number of overlapping interests.
The next step is to now use our method of get_overlapping to sort the list. Javascript provides a .sort() method, where we can supply a function to it which defines how it should sort.
If the supplied function (which has two parameters a and b, both representing elements in your array) returns a number less than 0, then it will make a come before b in the array, if the number returned is 0 then a and b will stay and if the number returned is greater than 0 then b will come before a. Using this idea, we can sort the people array by using the following:
get_overlapping(b) - get_overlapping(a)
where b now represents the interests array of one object in the array, and a represents the interests array of another object in your array
Using this idea, we can use this in your getSortedList method:
const people = [
{ name: "Ahmad", interests: ["shopping", "painting", "cooking"]},
{ name: "Betty", interests: ["running", "painting", "football"] },
{ name: "Charlie", interests: ["gaming", "football", "painting"] },
{ name: "Diana", interests: ["fishing", "singing", "driving"] },
{ name: "Ethan", interests: ["gaming", "clubbing", "cooking"] },
{ name: "Farhan", interests: ["cooking", "driving", "fishing"] },
{ name: "Gwen", interests: ["singing", "fishing", "gaming"] },
{ name: "Helen", interests: ["football", "clubbing", "shopping"] },
{ name: "Imelda", interests: ["painting", "running", "football"] },
{ name: "Josef", interests: ["shopping", "running", "cooking"] },
{ name: "Khan", interests: ["fishing", "running", "clubbing"] },
{ name: "Lionel", interests: ["gaming", "singing", "driving"] }
];
const sharon = {
name: "Sharon",
interests: ["football", "painting", "gaming"]
};
const get_overlapping = interests =>
sharon.interests.filter(i => interests.includes(i)).length;
const getSortedList = () => {
const output = people.slice(); // make a copy of people so that we don't modify the people array
output.sort(({interests: a}, {interests: b}) => get_overlapping(b) - get_overlapping(a));
return output; // return sorted array
}
console.log(getSortedList());
If you are looking to implement a sorting method yourself, there are plenty of sorting algorithms out there which you can take a shot at writing. Some are easier to implement and understand than others (eg: selection sort, insertion sort, etc...), however, they may not be as efficient to implement.

Categories