Sorting array of object inside reduce in JavaScript - javascript

I am trying to do sort inside the reduce and I thought I have everything correct but still my result is not sorted as desired.
Here is the code snippet I have:
var studentInfo = [
{
studentId: 1,
addresses: [
{street: '123 Main St'},
]
},
{
studentId: 2,
addresses: [
{street: '456 Front St'}
]
},
{
studentId: 3,
addresses: [
{street: '100 MLK St'}
]
}
];
function appendAddress(studentId, newAddress) {
return studentInfo.reduce(function (info, student) {
if (student.studentId === studentId) {
student = {
studentId: student.studentId,
addresses: student.addresses.concat(newAddress).sort(function (address1, address2) {
return address2.street - address1.stree;
})
};
}
info.push(student);
return info;
}, []);
}
var newAddress = {
street: '166 Devil St'
}
console.log('Result: ' + JSON.stringify(appendAddress(2, newAddress)));
I am getting result as
Result: [{"studentId":1,"addresses":[{"street":"123 Main St"}]},{"studentId":2,"addresses":[{"street":"456 Front St"},{"street":"166 Devil St"}]},{"studentId":3,"addresses":[{"street":"100 MLK St"}]}]
instead of
Result: [{"studentId":1,"addresses":[{"street":"123 Main St"}]},{"studentId":2,"addresses":[{"street":"166 Devil St"},{"street":"456 Front St"}]},{"studentId":3,"addresses":[{"street":"100 MLK St"}]}]
Am I missing anything?

As to the sorting issue, if that was the main thing you were wondering about, you do indeed have a typo as the comment above noted, and also, performing subtraction on non-numeric strings won't get you very far. I used .localeCompare in the solution above.
If you wanted to copy the objects as you were appending, that can still be done more simply, but I don't know if that's what you actually want.
var studentInfo = [
{studentId: 1,addresses: [{street: '123 Main St'}]},
{studentId: 2,addresses: [{street: '456 Front St'}]},
{studentId: 3,addresses: [{street: '100 MLK St'}]}
];
console.log(addAddress(2, {street: "1234 56th Ave"}));
function addAddress(studentId, address) {
const idx = studentInfo.findIndex(o => o.studentId === studentId);
if (idx !== -1) {
return [...studentInfo.slice(0, idx), {
studentId,
addresses: [...studentInfo[idx].addresses, address].sort((a,b) => a.street.localeCompare(b.street))
}, ...studentInfo.slice(idx+1)];
} else {
return [...studentInfo, {studentId, addresses:[address]}];
}
}
But now you're having two different copies of the data with some shared objects.

Related

transform an array of objects with map( )

I can't understand how the map () method works because all the examples are with numbers and to understand I need an example with something more specific.
so I made this
I have an array of objects:
let people = [
{
id: 1,
name: 'jhon',
last_name: 'wilson'
},
{
id: 2,
name: 'maria',
last_name: 'anyway'
},
{
id: 3,
name: 'lastOne',
last_name: 'example'
}
];
I want to understand how with people.map(); i can change the idk, name?? of the 2nd element.
this is how i think map() work:
people.map(() => {
people[1].name = prompt()
// At this point i don't know how continue
})
I'm studying on my own, so I will be very grateful to you :)
The .map() function will go through the entire array, and on each step of that process it will take the current item that we are looking at and will pass it as a parameter into the function. You can then do whatever you want to that item, and whatever you return from your function will replace what is in that position in the array.
Say for example, with the array you gave in your question, we wanted to remove the name and last_name properties, and combine them into a full_name property. We can do the following:
let people = [
{
id: 1,
name: 'jhon',
last_name: 'wilson'
},
{
id: 2,
name: 'maria',
last_name: 'anyway'
},
id: 3,
name: 'lastOne',
last_name: 'example'
}
];
people = people.map((person) => {
return {
id: person.id,
full_name: `${person.name} ${person.last_name}`
}
});
After this code runs, our people array would look like this:
[
{
id: 1,
full_name: 'jhon wilson'
},
{
id: 2,
full_name: 'maria anyway'
},
id: 3,
name: 'lastOne example'
}
];
You can think of it as doing something very similar to this:
function transformPerson(person) {
return {
id: person.id,
full_name: `${person.name} ${person.last_name}`
}
}
let newPeople = [];
for (let i = 0; i < people.length; i++) {
newPeople[i] = transformPerson(people[i])
}
people = newPeople;
Array.map() takes in a function as a parameter, passes each item of the array into the function, and returns an array of the result.
For example, if I wanted to multiply each of the items in the array by 2:
const x = [1, 2, 3, 4, 5]
const y = x.map(v => v * 2) // result: [2, 4, 6, 8, 10]
Note: Array.map does not affect the original array; it creates a new array of the results.
You could change your code to
let people = [{id:1,name:'john',last_name:'wilson'},{id:2,name:'maria',last_name:'anyway'},{id:3,name:'lastOne',last_name:'example'}];
people = people.map((p,i) =>({...p,name: i===1?prompt("New name"):p.name}))
console.log(people);
This will prompt the user only for a new name when i===1. The expression will create a new array that will be stored under the variable name people again. If you wanted people to remain unchanged you could assign the return value of the people.map()-call to a different variable (or constant).

Adding values to an object within an Array inside a FOR loop

I have an array extracted from Mongo in the following form
[
{
_id: 60d51d210e5e4e297066132a,
MemberName: 'Name of Member',
MemberRank: 25,
MemberFDR: 6.43,
MemberImageurl: 'uploads/images/gauravverma.jpg'
},
{
_id: 60d5c619c163f23195e01d00,
MemberName: 'Name Of Member',
MemberRank: 24,
MemberFDR: 6.5,
MemberImageurl: 'uploads/images/shashikhanna.jpeg'
},
]
After extracting the original array, I am looping through the array, extracting the name of the member and then doing some more queries in the DB. The length of this returned query, is the count and I want to add it in the original object like so
[
{
_id: 60d51d210e5e4e297066132a,
MemberName: 'Name of Member',
MemberRank: 25,
MemberFDR: 6.43,
MemberImageurl: 'uploads/images/gauravverma.jpg',
Count: 3(whatever the length of the array will be)
},
{
_id: 60d5c619c163f23195e01d00,
MemberName: 'Name Of Member',
MemberRank: 24,
MemberFDR: 6.5,
MemberImageurl: 'uploads/images/shashikhanna.jpeg'
Count: 5(whatever the length of the array will be)
},
]
My query returns the value perfectly, I am struggling with how to insert the value in the original object.
let memberName
let countOfCurrentChallengeMatches
for(let i=0; i<challengeList.length; ){
console.log("hi i am here 1")
memberName = challengeList[i].MemberName
console.log(memberName)
try {
console.log(memberName)
countOfCurrentChallengeMatches = await MatchRegister.find({
$and: [
{ $or: [{ChallengingPlayer: memberName},{ChallengedPlayer: memberName}] },
{ $or: [{ChallengeStatus: 'Awaiting Score Approval'},{ChallengeStatus: 'Accepted'},{ChallengeStatus: 'Completed'}, {ChallengeStatus: 'Issued'}] },
{ChallengerMonth: cMonth},
],
},'_id ChallengingPlayer ChallengedPlayer ChallengerMonth ChallengerYear ProposedChallengeDate ProposedChallengeTime ChallengeMatchLocation ChallengeStatus MatchFormat RejectionReason')
.sort({ProposedChallengeDate: 1}).exec()
} catch (err) {
const error = new HttpError(
'Something went wrong, could not update member.',
500
);
return next(error);
}
// Here is where i want to insert the value in the object
i++
}
I have tried options like, push, add and a few other options from google, but nothing works.
Just example below. Have you tried this example yet?
var arrOfObj = [{
name: 'eve'
}, {
name: 'john'
}, {
name: 'jane'
}];
var result = arrOfObj.map(function(el) {
var o = Object.assign({}, el);
o.isActive = true;
return o;
})
console.log(arrOfObj);
console.log(result);
Hey this simple line worked. Not sue why I missed it in my research
challengeList[i].count = countOfCurrentChallengeMatches.length

Grouping JSON by values

Using the Lever job posting API, I'm getting JSON results sorted by location, and I'm trying to figure out how to group all those results by "team" within the results, like the Shopify careers page.
Here is the codepen and here is the JSON
I tried adding the following on line 38 of the codepen to try to grab the team values, but it's not outputting as expected (I'm getting one letter per line, which isn't helpful):
for (var x in _data[i].postings[j].categories.team)
I'm sure it's probably something super simple, but I'm definitely not a javascript guy. Any help would be much appreciated!
Assume , the JSON output is
outJSON =
[ {
team: "TeamA",
name: "Ahmed",
field3:"val3"
},
{
team: "TeamB",
name: "Ahmed",
field3:"val43"
},
{
team: "TeamA",
name: "Ahmed",
field3:"val55"
},
]
Then see the groupBy function in the DEMO below:
DEMO :
outJSON = [{
team: "TeamA",
name: "Ahmed",
field3: "val3"
}, {
team: "TeamB",
name: "Ahmed",
field3: "val43"
}, {
team: "TeamA",
name: "Ahmed",
field3: "val55"
}]
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var groubedByTeam = groupBy(outJSON, 'team')
console.log(groubedByTeam);
Then , if you want to loop through categories (teams), get all categories in array :
Object.keys(groubedByTeam) // return ["TeamA","TeamB"]
then :
Object.keys(groubedByTeam).forEach(function(category) {
console.log(`Team ${category} has ${groubedByTeam[category].length} members : `);
groubedByTeam[category].forEach(function(memb,i){
console.log(`---->${i+1}. ${memb.name}.`)
})
});

Compare value and add if matches to an array

Im trying to merge 2 data sources in 1, I wanna loop through them and if a specefic value matches than add it to the first object with the same value and add the in the emty array what is already there. No matter how much objects I have.
So lets say I have this information
Source 1
one = {
"teams": [
{
name: 'ABC',
members: [],
rooms: '0'
},
{
name: 'DEF',
members: [],
rooms: '1'
}
]
}
Source 2
two = {
"persons": [
{
name: 'Foo',
gender: 'male',
room: '1'
},
{
name: 'Bar',
gender: 'female',
room: '2'
}
]
}
And what I want is that the 'persons' array merge to the members array if the 'room and rooms' value matches.
What I would assume is something similar like this:
for(var i = 0 ; i < two.persons.length; i++) {
if (one.teams[i].rooms == two.persons[i].room) {
data.teams[i].members.push(two.persons[i]);
}
}
using higher order methods you can do:
one = {
"teams": [
{
name: 'ABC',
members: [],
rooms: '0'
},
{
name: 'DEF',
members: [],
rooms: '1'
}
]
};
two = {
"persons": [
{
name: 'Foo',
gender: 'male',
room: '1'
},
{
name: 'Bar',
gender: 'female',
room: '2'
}
]
};
var ttt = one.teams.map(function(x){
var roomVal= x.rooms;
x.members = two.persons.filter(function(t){
return t.room == roomVal});
return x;
})
one.teams = ttt;
console.log(one)
The problem with your code is that once you iterate the two array, then you do not go back and see if the previous element matched with the current one.
For example, if [0] on each arrays does not match and you iterate to index [1] in the for-loop, you do not have a way to check if two[1] matched one[0].
To do a complete search, you could directly iterate the arrays for each value of two:
two.persons.forEach(function(person) {
one.teams.forEach(function(team) {
if (team.rooms == person.room) {
team.members.push(person);
}
});
});
There are many strategies to do this. But most important you should iterate each array separately. I would use an Array.forEach();
one.teams.forEach(function (team, teamsIndex, teamsArray) {
two.persons.forEach(function (person, personsIndex, personsArray) {
if (team.room == person.room) {
// Do what you need to do.
}
});
});
Didn't check syntax so be aware to read Array.forEach(); documentation.

$filter by nested object values only

I am filtering JSON data against an array of strings. An example of $scope.Data is below:
{
"exerciseDescription": "Lean forward onto a chair and allow the arm to hang down, gently swing the arm from side to side",
"exerciseID": "324",
"exerciseName": "Shoulder Pendular Exercise (Adduction/Abduction)",
"images": [
1008,
1009,
1010
],
"tags": [
"Body Part",
"Arm",
"Upper body",
"Equipment",
"Chair"
"Soft Tissue"
]
},
There is a total of 4500 sets of this data and I would like to filter it by clicking on checkboxes. On clicking a checkbox I push the value of the checkbox (which will be a tag) to an array.
I would then like to filter against ONLY the the nested tag values.
My watch function is here:
$scope.$watchCollection('ActiveFilters', function(newValue) {
if ($scope.ActiveFilters.length > 0) {
$scope.FilteredData = $scope.Data;
for (var i = 0; i < $scope.ActiveFilters.length; i++) {
$scope.FilteredData = $filter('filter')($scope.FilteredData, $scope.ActiveFilters[i]);
console.log($scope.FilteredData);
// console.log($scope.ActiveFilters);
}
}
else {
$scope.FilteredData = [];
}
});
So if $scope.FilteredData contains any 'ActiveFilters' in its nestedtag` array then it will show in the scope.
In brief - how can I only filter against the nested tag array.
Filtering by an value of object in array in javascript:
var items = [{
"exerciseDescription": "Lean forward onto a chair and allow the arm to hang down, gently swing the arm from side to side",
"exerciseID": "324",
"exerciseName": "Shoulder Pendular Exercise (Adduction/Abduction)",
"images": [
1008,
1009,
1010
],
"tags": [
"Body Part",
"Arm",
"Upper body",
"Equipment",
"Chair",
"Soft Tissue"
]
}];
var filter = function (obj) {
if (obj['tags'].indexOf("Soft Tissue") != -1) { // <--- filter by value in tags array
return true;
}
return false;
}
var filtered = items.filter(filter);
I think you will get the idea and adapt it to use with angular.
I don't think there's a good reason to use $filter unless you're trying to filter in your AngularJS markup/templates. It's only really useful in JS if you want to support AngularJS filter expressions in custom directives.
Here's a more complete example for doing a tag filter in vanilla JavaScript:
var filterTags = ['foo', 'bar', 'baz'];
var incomingData = [ // simplified example
{id: 'banana', tags: ['foo', 'qux']},
{id: 'potato', tags: ['qux', 'baz', 'foo', 'bar']},
{id: 'carrot', tags: ['qux', 'quux']},
{id: 'cabbage', tags: ['foo', 'baz', 'bar']}
];
var dataMatchingAllTags = incomingData.filter(function (obj) {
return filterTags.every(function (tag) {
return obj.tags.indexOf(tag) !== -1;
});
}); // [{id: 'potato', …}, {id: 'cabbage', …}]
var dataMatchingAnyTag = incomingData.filter(function (obj) {
return filterTags.some(function (tag) {
return obj.tags.indexOf(tag) !== -1;
});
}); // [{id: 'banana', …}, {id: 'potato', …}, {id: 'cabbage', …}]
var dataMatchingTagsExactly = incomingData.filter(function (obj) {
return (
obj.tags.length === filterTags.length &&
filterTags.every(function (tag) {
return obj.tags.indexOf(tag) !== -1;
})
);
}); // [{id: 'cabbage'}]
In your case $scope.ActiveFilters would be filterTags, $scope.Data would be incomingData and $scope.FilteredData would be either of dataMatchingAllTags, dataMatchingAnyTag or dataMatchingTagsExactly, depending on how you want the filters to work.
Note that this example assumes ES5 but considering AngularJS does so as well, I don't think this will be a problem.

Categories