javascript remove using reduce - javascript

I'm trying to just get particular values in an obj with the status of 'xyz'
user.friends =
CoreMongooseArray [
{ _id: 5b11824350c0011afe9ca310,
user:
{ profile: [Object],
_id: 5b11805a50c0011afe9ca2fe,
username: 'user1' },
status: 'abc' },
{ _id: 5b191033d240ab4a10ffb54f,
user:
{ profile: [Object],
_id: 5b0ec81f958f5b4919b83c40,
username: 'user2' },
status: 'xyz' } ]
I'm using
user.friends.reduce((a, t) => a + (t.type === 'xyz' ? 0 : 1), 0);
but it only returns 1, rather than just the object with the username user2, why?

but it only returns 1, rather than just the object with the username user2, why?
Because that's the last return value of your callback. This is how reduce works.
If you want to find one particular object in an array matching a criterion, reduce isn't the tool to use (though you could force it to work, just as you can use a screwdriver as a hammer if you try hard enough). The tool to use is find:
const oneXYZFriend = user.friends.find(e => e.status === 'xyz');
(Notice I changed the test from e.type === 'xyz' to e.status === 'xyz', since your objects have a status property, not a type property.)
If you want to find several objects in an array matching a criterion, you'd use filter:
const xyzFriends = user.friends.filter(e => e.status === 'xyz');

Related

Compare two different array of objects and return boolean

I pursuing my 12th grade, trying hard to learn react and javascript, I am doing some basic exercise like comparing array of objects and returning true or false if the id matches, I found some workable solutions for the same but all are returning me back the difference of that two array objects I need to see boolean (true) if value matches else false. I am struggling to get this I am confused with the retzurn type of the filter and some methods in array. Could anyone help me out to achieve this output? Thanks in advance!
const a = [
{ id: '1234', description: 'PrdOne', version: '3', categories: '--' },
{ id: '12345', description: 'PrdTwo', version: '2', categories: '--' },
{ id: '123456', description: 'PrdThree', version: '2', categories: '--' },
];
const b = [
{ id: '1234', description: 'PrdOne', version: '3', categories: '--' },
{ id: '12345', description: 'PrdTwo', version: '2', categories: '--' },
];
let result = a.filter(o => !b.some(v => v.id === o.id));
console.log(result);
It's giving me the difference but I need boolean value (true) if there is a match else false
The method filter will remove items inside your array 'a' each time the condition inside the filter will return false. If the method inside your filter method return true, it will pass over the iteration.
So what you need is to use some() method instead of filter, like this :
let result = a.some(o => !b.some(v => v.id === o.id));
If you want to know the return of your condition of each item of your array, just replace some() method by map() :
let result = a.map(o => !b.some(v => v.id === o.id));
This will replace each item of a by the return of the matching condition.

Getting undefined result when trying to loop and filtering array

I am currently working with objects and arrays in nodejs in conjuction with filters. I am currenlty facing difficulty in figuring out how to properly traverse an object and filter for the desired results. Instead I am getting undefined. I have an object users and I am wanting to filter for each user configuration that has active === true and then ultimately display every users configuration with that filter in the final result. What is the right/best way to approach this? Should I use map?
Current Result:
undefined
Desired Result:
[
{
email: 'test1#email.com',
active: true
},
{
email: 'test3#email.com',
active: true
},
{
email: 'test4#email.com',
active: true
}
]
Code:
const users = [
{
name: 'User1',
configuration: [
{
email: 'test1#email.com',
active: true
},
{
email: 'test2#email.com',
active: false
}
],
},
{
name: 'User2',
configuration: [
{
email: 'test3#email.com',
active: true
},
{
email: 'test4#email.com',
active: true
}
],
},
];
const result = users.forEach(user => user.configuration.filter( x => {
let {
active
} = x;
return active === true;
}));
console.log(result);
you can use flatMap for this. forEach always returns undefined. usually if you want to return some array use map but since filter also returns an array you need to flat it to get your desired result hence flatMap
const users = [{name: 'User1',configuration: [ {email: 'test1#email.com',active: true},{email: 'test2#email.com',active: false}],},{name: 'User2',configuration: [ {email: 'test3#email.com',active: true},{email: 'test4#email.com',active: true}],},];
const result = users.flatMap(user => user.configuration.filter( x => x.active===true));
console.log(result);

React native filter array not working on string

I am having issues trying to filter an array of message objects. I do not want to include the objects with "_id" value "receiver" or "blockedUsers".
Array of message objects:
[
{"_id":"receiver"},
{"_id":"blockedUsers"},
{"_id": MjIzx3XA1mpcuzgDVZj","createdAt":1631349363111,"text":"Ok","user":{"name":"Nikki","_id":"M6fBsPludfYVjJXKYvwgxHRacYw1"}},
{"_id":" MjG3hFAgcNweJWh9SF7","createdAt":1631300277391,"text":"Again","user":{"name":"Chris","_id":"tFhmw5oQoPhk8nF2sx5rE5BFqw93"}
}
]
The following doesn't seem to work
this.state.messages.filter(msg => !msg._id.includes("receiver") || !msg._id.includes("blockedUsers")).
The original array is returned.
But if I use this
this.state.messages.filter(msg => msg._id.includes("receiver") || msg._id.includes("blockedUsers"))
it returns:
[{"_id":"receiver"},{"_id":"blockedUsers"}]
Can you please assist?
The filter condition is wrong, it will always return true in your case.
Array.filter() callback should return true for the items you want to keep.
You have to use AND && instead of OR ||.
this.state.messages.filter(msg =>
!msg._id.includes("receiver") && !msg._id.includes("blockedUsers")
)
Why it initially returned true? Because when the id is "receiver", it doesn't include "blockedUsers" and the other way around. You want to keep the element if it doesn't include "receiver" AND it also doesn't include "blockedUsers".
Here is a truth table to make it more clear, it should be true for the items you want to keep.
!includes("receiver")
!includes("blockedUsers")
OR
AND
false
false
false ✅
false ✅(skip item, it includes both strings)
false
true
true ❌
false ✅(skip item, it includes "blockedUsers")
true
false
true ❌
false ✅(skip item, it includes "receiver")
true
true
true ❌
true ✅(this is the only case where you want to keep the item, when it doesn't include either string)
You can combine Array.prototype.filter() with Array.prototype.includes():
const arr = [{ _id: 'receiver' },{ _id: 'blockedUsers' },{ _id: 'MjIzx3XA1mpcuzgDVZj', createdAt: 1631349363111, text: 'Ok', user: { name: 'Nikki' } },{ _id: 'M6fBsPludfYVjJXKYvwgxHRacYw1' },{ _id: ' MjG3hFAgcNweJWh9SF7', createdAt: 1631300277391, text: 'Again', user: { name: 'Chris', _id: 'tFhmw5oQoPhk8nF2sx5rE5BFqw93' }}]
const result = arr.filter(({ _id }) => !['receiver', 'blockedUsers'].includes(_id))
console.log(result)

Comparing two arrays with field in common then pushing to a new array with corresponding grouped fields

general programming problem here.
I have this array called SPACES
[
{
_id: 5e1c4689429a8a0decf16f69,
challengers: [
5dfa24dce9cbc0180fb60226,
5dfa26f46719311869ac1756,
5dfa270c6719311869ac1757
]
},
{
_id: 5e1c4eb9c9461510407d5e81,
challengers: [ 5dfa24dce9cbc0180fb60226, 5dfa26f46719311869ac1756 ],
}
]
And this array called USERS
[
{
_id: 5dfa24dce9cbc0180fb60226,
name: 'Account 1',
email: 'account1#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ],
},
{
_id: 5dfa26f46719311869ac1756,
name: 'Account 2',
email: 'account2#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ]
},
{
_id: 5dfa270c6719311869ac1757,
name: 'Account 3',
email: 'account3#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69 ]
}
]
What I want to do, is go through both, and instead of having the SPACES.challengers array be just IDS, I would like the array to contain each USER object.
So for example, if the USER has an ID that is inside the SPACES.challengers array, then push the user into that array (which will then be the entire object).
SO FAR I have tried this (I am not very good yet):
users.map( ( user ) => {
spaces.map( ( space ) => {
if ( user.spaces.includes( space._id ) ) {
space.challengers.push(user)
}
} );
} );
However, I am not getting inside the IF block. (Even if I did, not sure if it would work OR if this is even how to do it). It feels Odd doing double maps, as I get so many iterations, and it duplicates my push (cause I have no logic to see if it just has been pushed).
Assuming every entry in the Users array has a unique ID, we can build a Hashmap to store (id, index) pairs in order to search efficiently for an ID from Users array while looping through Spaces array.
let spaces = [{_id: '5e1c4689429a8a0decf16f69',challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757']},{_id: '5e1c4eb9c9461510407d5e81',challengers: [ '5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756' ],}]
let users = [{_id: '5dfa24dce9cbc0180fb60226',name: 'Account 1',email: 'account1#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ],},{_id: '5dfa26f46719311869ac1756',name: 'Account 2',email: 'account2#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ]},{_id: '5dfa270c6719311869ac1757',name: 'Account 3',email: 'account3#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69' ]}]
let IDIndexMapping = {} // To store (_id, index) pairs, in order to improve search efficiency
for(let index in users) // Iterate through Users array using index
IDIndexMapping[users[index]._id] = index; // store (_id, index) pair in IDIndexMapping
// I'm avoiding using `map` and using vanilla `for` loop for space efficiency
// as map returns a new array but with `for` loop, we can perform changes in-place
for(let outerIndex in spaces){ // Iterate through `spaces` array using index
let challengers = spaces[outerIndex].challengers; // Get challengers array
for(let innerIndex in challengers){ // Iterate through challengers array using index
let ID = challengers[innerIndex]; // Get ID
if(ID in IDIndexMapping) // If ID exists in IDIndexMapping
spaces[outerIndex].challengers[innerIndex] = users[IDIndexMapping[ID]]; // Change ID to actual User object
}
}
console.log(spaces)
Output
[ { _id: '5e1c4689429a8a0decf16f69',
challengers: [ [Object], [Object], [Object] ] },
{ _id: '5e1c4eb9c9461510407d5e81',
challengers: [ [Object], [Object] ] } ]
.map and .find should work here. keep it simple.
var spaces = [
{
_id: "5e1c4689429a8a0decf16f69",
challengers: [
"5dfa24dce9cbc0180fb60226",
"5dfa26f46719311869ac1756",
"5dfa270c6719311869ac1757"
]
},
{
_id: "5e1c4eb9c9461510407d5e81",
challengers: ["5dfa24dce9cbc0180fb60226", "5dfa26f46719311869ac1756", "some non existent"]
}
],
users = [
{
_id: "5dfa24dce9cbc0180fb60226",
name: "Account 1",
email: "account1#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa26f46719311869ac1756",
name: "Account 2",
email: "account2#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa270c6719311869ac1757",
name: "Account 3",
email: "account3#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69"]
}
],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(challenger =>
users.find(user => user._id === challenger)
).filter(row => row)
}));
console.log(JSON.stringify(result, null, 2));
You can create a map of challengers for look-up and then put them in spaces.
//create user map for look-up
userMap = users.reduce((res, val) => ({
...res,
[val._id]: val
}), {});
//change challenger id with user object
inflatedSpaces = spaces.map(s => ({ ...s, challengers: s.challengers.map(c => userMap[c]) }));
You could map the users with a Map.
Beside the destructuring of the object for mapping this answer uses for this part
challengers: challengers.map(
Map.prototype.get, // cb with a prototype and using `this`
new Map(users.map(o => [o._id, o])) // thisArg
)
the above mentioned Map in two parts.
The lower part generates an instance of Map where _id of the users items is used as key and the whole object as value. This instance is uses as thisArg of Array#map, the second parameter.
The upper part is a prototype of Map, used as callback. And while an this object is supplied, a binding (Function#bind) is not necessary.
var spaces = [{ _id: '5e1c4689429a8a0decf16f69', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757'] }, { _id: '5e1c4eb9c9461510407d5e81', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756'] }],
users = [{ _id: '5dfa24dce9cbc0180fb60226', name: 'Account 1', email: 'account1#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] }, { _id: '5dfa26f46719311869ac1756', name: 'Account 2', email: 'account2#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] },{ _id: '5dfa270c6719311869ac1757', name: 'Account 3', email: 'account3#gmail.com', spaces: ['5e1c4689429a8a0decf16f69'] }],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(
Map.prototype.get,
new Map(users.map(o => [o._id, o]))
)
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

What is the best approach to clone objects properties when using map()?

I want to add a new property (contactDetails.countryName) and assign a value to a nested object stored in an array called users using the function map().
I've recently learned that I should use the spread operator (...) and then create / assign the new property in order to avoid mutating my original array of objects so I've developed 2 different implementations for this but I'm not really confident I'm following the best practices to accomplish I want to regarding the semantic and performance.
What would be the best approach to accomplish what I want to do in your opinion?
const countries = [
{ id: 3, countryName : "UK" },
{ id: 4, countryName : "Spain" },
{ id: 6, countryName : "Germany"}
];
const users = [
{ id : 1,
name: "Douglas Camp",
dateOfBirth: "23-06-1984",
contactDetails:
{
country: 3,
phone: "7373724997"
}
},
{
id : 2,
name: "Martin Stein",
dateOfBirth: "19-08-1992",
contactDetails:
{
country: 6,
phone: "3334343434"
}
},
];
const usersData = users.map(user=> {
// Version 1 : using spreading operator twice
const newUser = {
...user,
contactDetails: {
...user.contactDetails,
countryName: countries.find(c=> c.id == user.contactDetails.country).countryName
}
};
return newUser;
});
// Version 2: copying the original object property and using spread operator only for cloning the nested object properties
const newUser = {
id: user.id,
name: user.name,
dateOfBirth: user.dateOfBirth,
contactDetails: {
...user.contactDetails,
countryName: countries.find(c=> c.id == user.contactDetails.country).countryName
}
};
console.log(users);
console.log(usersData);
Here is an approach you can consider:
First of all I would Array.reduce the countries to a Map so you can get them via key/value or in this case by countries.get(key) and avoid filtering that array every time.
You can map through the users and for each one create a new object. In this case I call them accounts.
You can also consider using Object.assign
Note that both ... operator and Object.assign are shallow clone approaches. They do not recursively clone the nested objects/children. For that you can use JSON.stringify and JSON.parse etc.
let countries = [
{ id: 3, countryName : "UK" },
{ id: 4, countryName : "Spain" },
{ id: 6, countryName : "Germany"}
].reduce((r,{id, countryName}) => (r.set(id, countryName), r), new Map()) // reduce with Map
let users = [ { id : 1, name: "Douglas Camp", dateOfBirth: "23-06-1984", contactDetails: { country: 3, phone: "7373724997" } }, { id : 2, name: "Martin Stein", dateOfBirth: "19-08-1992", contactDetails: { country: 6, phone: "3334343434" } }, ];
let accounts = users.map(user => Object.assign({}, user, { // <-- map through
contactDetails: {
...user.contactDetails,
countryName: countries.get(user.contactDetails.country) // <-- get by key
}
}))
users[0].id = 2 // <-- modify users
users[0].contactDetails.phone = "00000"
console.log(users, accounts) // <-- no changes to accounts
Notice when we update the users[0].id and users[0].contactDetails.phone the accounts values did not update.
I normally use version 1, the spread operator twice. I also would consider checking out immer which allows you to do mutable updates on a cloned draft and handles merging it back for you.
const newUser = immer(user, draft => {
draft.contactDetails.countryName = countries.find(
c => c.id == user.contactDetails.country).countryName
)
})
Just edit the specific property you want and immer handles copying the rest of it.
Cloning and merging MapsSection
Just like Arrays, Maps can be cloned:
var original = new Map([
[1, 'one']
]);
var clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison
I personally like to use Version 1, as it makes your code much less redundant and easier to read. It also passes all the properties of 'user' down to newUser.

Categories

Resources