I have this code:
let peopleInRoom = [];
for (let message of messages) {
for (let email of message.user.email) {
if (!peopleInRoom.includes(email)) {
peopleInRoom.push(email);
}
}
}
let peopleInRoomElement = peopleInRoom.map(person => (
<li>{person}</li>
))
Basically I am trying to get all the unique emails and display them.
Is there a shorter and more efficient way (maybe some ES6 features) to write the same code? Seems too much code than needed.
I looked at this answer: How to get distinct values from an array of objects in JavaScript?
EDIT: Above code does not do what I want.
My data looks like this:
[
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
The objects are all messages. And I want to get an array of all the unique emails from each message
You can use the Set object that is built into JavaScript. Set object actually keep the distinct primitive values.
const messages = [
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
const peopleInRoom = [...new Set(messages.map(message => message.user.email))];
It actually extracts the email from each message and then passes it to the Set object which only keeps the unique set of emails. After that, it will spread that Set to the array, since Set is also an iterable and returns the array of the people in room.
If I understand correctly, people have messages, messages have email addresses and the OP seeks the unique set of email addresses. If that's all the data available, then there's no alternative but to iterate it, checking to see if each email has been collected already, and collecting it if it hasn't been.
There are ways to conceal this work by doing it in library code. Probably the highest level utility is lodash's _.uniqueBy, but the work must be done one way or another.
The Set object enforces uniqueness of its elements. You can use it this way:
const peopleInRoom = Array.from(new Set(messages.map(message => message.user.email)));
First you can make an array of all the email addresses:
const data = [
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
const emailArray = data.map((elem) => {
return elem.user.email;
}
and then you can filter them to be unique:
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
emailArrayFiltered = emailArray.filter(onlyUnique);
see here for ref link
What about:
const peopleInRoom = messages.map(e => e.user.email && e.user.email);
console.log(peopleInRoom)
gives you this output:
["foo#bar.com", "baz#qux.com"]
Related
I am using React with nextJS to do web developer,I want to render a list on my web page, the list information comes from the server(I use axios get function to get the information). However some JSON objects are lack of some information like the name, address and so on. My solution is to use a If- else to handle different kind of JSON object. Here is my code:
getPatientList(currentPage).then((res: any) => {
console.log("Response in ini: " , res);
//console.log(res[0].resource.name[0].given[0]);
const data: any = [];
res.map((patient: any) => {
if ("name" in patient.resource) {
let info = {
id: patient.resource.id,
//name:"test",
name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
} else {
let info = {
id: patient.resource.id,
name: "Unknow",
//name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
}
});
Is there any more clever of efficient way to solve this problem? I am new to TS and React
Use the conditional operator instead to alternate between the possible names. You should also return directly from the .map callback instead of pushing to an outside variable.
getPatientList(currentPage).then((res) => {
const mapped = res.map(({ resource }) => ({
id: resource.id,
// want to correct the spelling below?
name: "name" in resource ? resource.name[0].given[0] : "Unknow",
birthDate: resource.birthDate,
gender: resource.gender,
}));
// do other stuff with mapped
})
I have my field names in an array like these:
const baseFields = [
'employeeNumber',
'firstName',
'lastName',
'trEmail',
'position'
];
Those are the input fields I only have to care about being validated.
In the request body, I receive an array of objects.
Example:
{employeeNumber: 12343,
firstName: Will,
lastName: Smith,
trEmail: smith#will.com,
position: Actor,
salary: low
},
{employeeNumber: 12344,
firstName: Chris,
lastName: Rock,
trEmail: rock#chris.com,
position: stuntman,
salary: ''
}
I want to validate this array with only the concern fields in the baseFields array.
This is my current validator code. I've found out that I can use wildcards in order to validate arrays.
const existsOptions = {
checkNull: true,
checkFalsy: true
};
const postRequiredFields = () => {
const validators = [];
const validator = body('*.*')
.exists(existsOptions)
.bail()
.isString();
validators.push(validator);
return validators;
};
Using this const validator = body('*.*') will check all of the fields in the array of objects in the body. Since I can get this message:
{ value: '',
msg: 'Invalid value',
param: '[1].salary',
location: 'body' }
You see, salary field is being checked. It returned invalid value because the second index in the array has the salary set to '' or empty. But again, the salary field is not one of the fields that I need to validate.
So I tried something like this body('baseFields*.*') to check the whole array of objects but only the concern fields but it won't work. I couldn't find the right wildcard pattern for my scenario online. The documentation also says very little.
to check an object in an array, use: *.key
and then you can just loop your keys and add them dynamically:
const postRequiredFields = () => {
const validators = [];
baseFields.map((key) => {
const validator = body(`*.${key}`)
.exists(existsOptions)
.bail()
.isString();
validators.push(validator);
});
return validators;
};
i have a JavaScript map with multiple values it is like this (i simplified the code for obvious reasons):
Map(2) {
'group1' => {
username: 'userTest',
dsId: '710300636817653790',
openDate: '2021-12-13 18:29:16'
},
'group2' => {
username: 'Juojo',
dsId: '477581625841156106',
openDate: '2021-12-13 18:29:23'
}
}
I want to get the username value (Juojo) of the second group of data. I tried doing this:
console.log(map.get(group2.username));
but this logs me "undefined", when i try without the ".username" (console.log(map.get(group2));) it replys me:
{
username: 'Juojo',
dsId: '477581625841156106',
openDate: '2021-12-13 18:29:23'
}
I only want the reply to be "Juojo"
Access the username property on the object returned by get.
console.log(map.get(group2).username);
If it is possible for the key to not exist, you can use the optional chaining operator, which will produce undefined in that case instead of causing an error.
console.log(map.get(group2)?.username);
You should try to get the object which has stored in your map then try to read the property of returned object, like below example:
const myMap = new Map([
['group1',{
username: 'userTest',
dsId: '710300636817653790',
openDate: '2021-12-13 18:29:16'
}],
['group2', {
username: 'Juojo',
dsId: '477581625841156106',
openDate: '2021-12-13 18:29:23'
}]
])
const userName = myMap.get('group2').username;
console.log(userName);
Trying to wrap my brain around how I should tackle filtering an array of objects and only returning results that satisfy all of the tags.
The tags could be any of the fields - fname/lname/email/position/etc
let search_tags = ['CEO', 'Steven'];
let contacts = [
{ fname: 'Steve', lname: 'Johnson', email: 'user#domain.com', position: 'CEO' },
{ fname: 'James', lname: 'Laurence', email: 'boss#domain.com', position: 'CFO' }
]
let results = contacts.filter((contact) => {
if (search_tags.includes(contact.fname) ||
search_tags.includes(contact.lname) ... ) {
return contact;
}
}
I shortened a bit of the code for brevity and obviously this solution will return contacts that match any search_tag but... I need to only return results that satisfy every search_tag.
It's been a long day and I have no one to talk this through so I'm hoping someone may be able to point me in the right direction or give me that ah-ha! moment I'm hoping for :)
Thanks in advance!
If you wanted to return one that matched every search tag you'd want to use && instead of || but that still leaves you with a bunch of verbose and duplicated code
Instead of operating directly on the contact Object, you can use Object.values() https://mdn.io/objectvalues which would give you an array of ['steve', 'johnson', 'user#domain]... etc.
Then you could in your filter:
contacts.filter((contact) => {
const contactValues = Object.values(contact);
// Return the search item if at least one item matches
// Would return true if at least one item matches
return contactValues.some(value => search_tags.includes(value));
// return true only if all search tags match
return contactValues.every(value => search_tags.includes(value));
}
Object.values is quite a new feature, so if you don't have it available in babel, then you can use Object.keys and grab the value using contact[someKey]
Array.prototype.filter() can be combined with Array.prototype.every(), Object.values() and Array.prototype.includes() to construct an Array of matches consisting solely of contact Objects that contain a matching value for every element in search_tags.
See below for a practical example.
// Search Tags.
const search_tags = ['CEO', 'Steven']
// Contacts.
let contacts = [
{ fname: 'Steven', lname: 'Johnson', email: 'user#domain.com', position: 'CEO' },
{ fname: 'James', lname: 'Laurence', email: 'boss#domain.com', position: 'CFO' }
]
// Matches.
const matches = contacts.filter((contact) => search_tags.every((tag) => Object.values(contact).includes(tag)))
// Log.
console.log(matches)
ES6:
function filterIt(arr, searchKeys) {
return arr.filter(obj => Object.keys(obj).some(key => searchKeys.includes(obj[key])));
}
Can spread operator solve below problem? Imagine I have more fields, then I have to declare req.body.something for every single fields, that's so tedious.
app.use((res,req,next) => {
const obj = {
name: req.body.name,
age: req.body.age,
gender: req.body.gender
}
//
User.saveUser(resp => res.json(resp)) //User model
})
You can use destructuring assignment:
const obj = req.body;
const { name, age, gender } = obj;
But, still you will have to validate it, and count all of them in your scheme.
Update:
Adding some validation example.
Assuming such schema in your route:
const tv4 = require('tv4');
const schema = {
type: 'object',
properties: {
name: 'string',
age: number,
gender: {
type: 'string',
pattern: /f|m/i
}
},
required: ['name']
};
And then, in your handler you validate:
if (tv4.validate(req.body, schema) {
// continue your logic here
} else {
// return 400 status here
}
You can use lodash's pick():
_.pick(object, [paths])
Creates an object composed of the picked object properties.
Example code is:
const _ = require('lodash');
...
const obj = _.pick(req.body, ['name', 'age', 'gender']);
If gender does not exist in req.body, it would be ignored -- the result obj object won't have a gender field.
If all the req.body fields are needed, you can just assign req.body to obj:
const obj = req.body;
To validate req.body content, you can use lodash's .has():
_.has(object, path)
Checks if path is a direct property of object.
Example code would be:
_.has(req.body, ['name', 'age', 'gender']); // return true if all fields exist.
You can use destructuring assignment
const { name, age, gender } = req.body
or if you wanna use spread operation, you can use :
const obj = { ...req.body }
Hope it helps!