Validate optional string with Yup? - javascript

I'm trying to validate a string with Yup:
const schema = object({
firstname: string().optional().nullable().notRequired().min(2),
});
The rules should be a string but can be null or empty, and if there is a value, then the length must be more than 2.
But for some reason, it's not working:
const shouldWorks = [
{ firstname: 'bla' },
{ firstname: '' }, <--- its failed here.. empty is okay (.notRequired)
{ firstname: null },
];
How do I change the schema to fit my rules?
stackblitz
import { object, string } from 'yup';
console.clear();
const shouldWorks = [
{ firstname: 'bla' },
{ firstname: '' },
{ firstname: null },
];
const shouldNotWork = [{ firstname: 'a' }];
const schema = object({
firstname: string().optional().nullable().notRequired().min(2),
});
shouldWorks.forEach((obj, i) => {
console.log(`test: ${i}`);
schema.validateSync(obj);
});
shouldNotWork.forEach((obj, i) => {
try {
schema.validateSync(obj);
console.log(`error test: ${i} failed`);
} catch (e) {
console.log(`error test: ${i} pass`);
}
});

You can use yup.lazy to lazily decide what validators to use based on the value:
const schema = object({
firstname: lazy((value) =>
value === ''
? string()
: string().optional().nullable().notRequired().min(2)
),
});
Stackblitz

Related

Retrieving data from user input and save it to mongoose model

This is my model shema:
const DemoSchema = new Schema({
rowOne: {
colOne: {
one: {
name: String,
qty: Number,
},
two: {
name: String,
qty: Number,
},
three: {
name: String,
qty: Number,
},
},
},
rowTwo: {
colOne: {
one: {
name: String,
qty: Number,
},
two: {
name: String,
qty: Number,
},
three: {
name: String,
qty: Number,
},
},
},
});
When I do this it saves data to my model:
const product = await new Regal({
rowOne: { colOne: { two: { name: productName, qty: productQty } } },
});
product.save();
My question is, how can I replace rowOne, colOne and two with user input?
I tried this:
const row = req.body.rows // this must be rowOne or rowTwo
const column = req.body.column // colOne or other (colOne to colNine)
const col = req.body.col // one, two or three
const productName = req.body.name
const productQty = req.body.qty
Attempt 1:
const product = await new Regal({ `${row}`: { `${column}`: { `${col}`: { name: productName, qty: productQty }}}});
'Error --> Property Assignment expected.'
Atempt 2:
const product = await new Regal(`{${row}: { ${column}: { ${col}: { name: ${productName}, qty: ${productQty} } } } }`);
'Error --> Parameter "obj" to Document() must be an object, got {rowOne: { colOne: { one: { name: Milk, količina: 250 } } } }'
You can use variables as keys of objects using square brackets.
let user = 'username';
let newObj = { [user]:'saloni' }
this will create an object like {username:saloni}.
This way you can replace rowOne, colOne, and two with user input by storing them in a variable and then use it.

Javascript - Optimize algorithm (complex data strcuture)

Introduction
I am implementing a method which inserts posts to the respective users posts lists in my map, sorted by date (recent posts first).
This is how I am structuring my data:
state = {
userId: {
posts: [
{ // object returned from my feeds algorithm in the server side
id,
userData: {
id,
},
date,
},
... more posts ...
],
},
... more users ...
}
In my algorithm, I just need to insert all the posts that are inside a given list
[
{ id: "post1", { userData: { id: "alex" }, date },
{ id: "post2", { userData: { id: "sara" }, date }
]
in the posts list of each respective user.
Problem
I also need to avoid inserting posts that already exists in my state, and I can't find a simple way to do it optimally.
Current code
This is my current implementation. I feel that this can be done easier and faster. Any help?
/*
Algorithm
*/
function addContents(state, contents, contentType, cached) {
const newState = state;
contents.forEach((content) => {
const { userData: { id: userId } } = content;
const prevUserState = state.get(userId);
const prevContents = prevUserState?.[contentType] ?? [];
const newContents = prevContents;
// TODO - Avoid inserting if already exists in prevContents! (check by **id**)
let inserted = false;
for (const [index, prevContent] of prevContents.entries()) {
// Replace
if (content.id === prevContent.id) {
newContents[index] = content;
inserted = true;
break;
}
// Insert in the correct order
if(content.date >= prevContent.date) {
newContents.splice(index, 0, content);
inserted = true;
break;
}
}
if (!inserted) {
newContents.push(content);
}
newState.set([
userId,
{
...prevUserState,
[contentType]: newContents
}
]);
});
// if(isEqual(state, newState)) return state; (deep compare to avoid re-renderizations because of state update)
return new Map([...newState]);
}
/*
Test
*/
(() => {
// State
const state = new Map([]);
// User ALEX
const userId1 = "alex";
const userPosts1 = [ // already sorted by date
{
id: "78q78w0w0",
userData: {
id: userId1,
},
date: new Date("10/26/1999 00:00:01")
},
{
id: "92uwdq092",
userData: {
id: userId1,
},
date: new Date("10/26/1999 00:00:00")
}
];
state.set(userId1, { posts: userPosts1 });
// User SARA
const userId2 = "sara";
const userPosts2 = [ // already sorted by date
{
id: "iipzxx115",
userData: {
id: userId2,
},
date: new Date("12/25/2003 03:30:10")
},
{
id: "Wxrr22232",
userData: {
id: userId2,
},
date: new Date("01/01/2000 17:44:41")
}
];
state.set(userId2, { posts: userPosts2 });
const newPosts = [
{
id: "OLDEST FOR ALEX!",
userData: {
id: userId1
},
date: new Date("10/25/1999 23:59:59")
},
{
id: "NEWEST FOR SARA!",
userData: {
id: userId2
},
date: new Date("01/05/2010 22:22:22")
},
{
id: "OLDEST FOR SARA!",
userData: {
id: userId2
},
date: new Date("10/25/1999 23:59:59")
}
]
addContents(state, newPosts, "posts");
console.log(state.get(userId1))
console.log(state.get(userId2))
})();
Note: As this method is implemented in a React's reducer, to manage complex states, I am returning a new Map, after deep comparing the previous and the new state, to produce UI re-renderizations.
UPDATE
I have implemented another version where I do what I need, but maybe, it can be more optimized.
function addContents(state, contents, contentType, cached) {
const newState = state;
const exists = {}; // optimization
for (const content of contents) {
const {
userData: { id: userId },
} = content;
const prevUserState = state.get(userId);
const prevContents = prevUserState?.[contentType] ?? [];
const newContents = prevContents;
if (cached) {
if (!exists[userId]) {
exists[userId] = prevContents.reduce((map, content) => {
map[content.id] = true;
return map;
}, {});
}
// Avoid inserting if necessary
if (exists[userId][content.id]) {
break;
}
}
// Insert the new content in the user's content list
console.log(`Inserting ${content.id}`);
let inserted = false;
for (const [index, prevContent] of prevContents.entries()) {
// Replace
if (content.id === prevContent.id) {
newContents[index] = content;
inserted = true;
break;
}
// Insert in the correct order
if(content.date >= prevContent.date) {
newContents.splice(index, 0, content);
inserted = true;
break;
}
}
if (!inserted) {
newContents.push(content);
}
newState.set([
userId,
{
...prevUserState,
[contentType]: newContents
}
]);
}
// if (isEqual(state, newState)) return state;
return new Map([...newState]);
}
/*
Test
*/
(() => {
// State
let state = new Map([]);
// User ALEX
const userId1 = "alex";
const userPosts1 = [ // already sorted by date
{
id: "78q78w0w0",
userData: {
id: userId1,
},
date: new Date("10/26/1999 00:00:01")
},
{
id: "92uwdq092",
userData: {
id: userId1,
},
date: new Date("10/26/1999 00:00:00")
}
];
state.set(userId1, { posts: userPosts1 });
// User SARA
const userId2 = "sara";
const userPosts2 = [ // already sorted by date
{
id: "iipzxx115",
userData: {
id: userId2,
},
date: new Date("12/25/2003 03:30:10")
},
{
id: "Wxrr22232",
userData: {
id: userId2,
},
date: new Date("01/01/2000 17:44:41")
}
];
state.set(userId2, { posts: userPosts2 });
const newPosts = [
{
id: "OLDEST FOR ALEX!",
userData: {
id: userId1
},
date: new Date("10/25/1999 23:59:59")
},
{
id: "NEWEST FOR SARA!",
userData: {
id: userId2
},
date: new Date("01/05/2010 22:22:22")
},
{
id: "OLDEST FOR SARA!",
userData: {
id: userId2
},
date: new Date("10/25/1999 23:59:59")
}
]
state = addContents(state, newPosts, "posts");
console.log(state.get(userId1))
console.log(state.get(userId2))
/*
Insert again!
*/
state = addContents(state, newPosts, "posts", true);
})();
use an object instead of an array:
This is the same concept of the normalizr library for redux: https://github.com/paularmstrong/normalizr
state = {
[user1Id]: {
posts: {
[post1Id]: {
id,
userData: {
id,
},
date,
},
[post2Id]: {
id,
userData: {
id,
},
date,
},
... more posts ...
},
},
... more users ...
}
This way you can easily access the object you want by its Id and check whether it exists or not just doing: if(state[23].posts[12])
if you need to iterate the users or a user posts use
object.keys(state).map(userId => ...)
or
object.keys(state[23].posts).map(postId => ...)
INSERT/UPDATE:
state[23].posts[newId]: { ...newPost}
I'm not able to follow what you are doing but I think this is what you are after.
You can do it to a oneline very easy.
newdata = [{ id: "post1", { userData: { id: "alex" }, date }]
if(!oldstates.find(d =>
d.id === newdata.id &&
d.userData.id === newdata.userData.id &&
d.date === newdata.date
)) {
oldstates.push(newdata)
}
// oneliner
if(!oldstates.find(d => d.id === newdata.id && d.userData.id === newdata.userData.id && d.date === newdata.date )) oldstates.push(newdata)

Filter out array of object from array of object

I have the following data
const[myData,setMyData]=React.useState([])
React.useEffect(()=>{
let initialData=[{ email: 'user1#mail.com', date: '22-03-2020' },
{ email: 'user2#mail.com', date: '22-03-2021' },
{ email: 'user3#mail.com', date: '22-03-2021' }]
setMyData(initialData)
},[])
this data are displayed in a table with a checkbox and I have this function called everytime I push a button (after having the rows selected)
function add(datatoRemove ){
alert("datatoRemove "+JSON.stringify(datatoRemove ) )
let myArrayFiltered = myData.filter((el) => {
return datatoRemove .some((f) => {
return f.email!== el.email
});
});
alert("filtered"+JSON.stringify(myArrayFiltered ) )
setMyData( [...myArrayFiltered ])
}
The issue is that with one selection the "myArrayFiltered " returns the other 2 rows and the state is updated correctly but if I select two or all the row nothing changed and the myArrayFiltered have all 3 elements.
What am I missing here?
the final goal is:
possible scenario 1:
if
datatoRemove = [{ email: 'user1#mail.com', date: '22-03-2020' },
{ email: 'user2#mail.com', date: '22-03-2021' },
{ email: 'user3#mail.com', date: '22-03-2021' }]
then
myArrayFiltered=[] and also the myData= []
possible scenario 2:
if datatoRemove = [ { email: 'user2#mail.com', date: '22-03-2021' },{
email: 'user3#mail.com', date: '22-03-2021' }]
then
myArrayFiltered= [{ email: 'user1#mail.com', date: '22-03-2020' }]
and also myData.
You need to use this instead
let myArrayFiltered = initialData.filter((el) => {
return datatoRemove.every((f) => {
return f.email!= el.email
});
});

Angular & Mongo query , return result closely equal to related to the search keyword

If text(firstname) == 'Eric' , it should return results . Any idea how we can tweek my code to return result as expected on the example ?
I am using angular on the front-end.
#Expected result (this would be top on the result)
Eric Gluthner
Eric Lecher
Erick Laspin
#and the other names with eric which would be less prio
Derick Ramp
Daniel Ericto
Raerick Fouler
#Code
getData(text: string): Observable<Identity[]> {
const search_query = {
query: {
$or: [
{ firstName: { $like: text } },
{ email: { $like: text } },
],
status: 1,
$sort: {
firstName: 1
},
// $limit: 5,
}
};
Using $regex pattern matching
getData(text: string): Observable<Identity[]> {
const search_query = {
query: {
$or: [
{ firstName: { $regex: RegExp('^' + text) } },
{ email: { $like: text } },
],
status: 1,
$sort: {
firstName: 1
},
// $limit: 5,
}
};

Javascript Can't catch an error inside map function

let's say I have a usersList like this:
var usersList = [
{ firstName : 'Adam', lastName: 'Yousif', age: 23 },
{ firstName : 'Mohamed', lastName: 'Ali' },
{ firstName : 'Mona', lastName: 'Ahmed', age: 19 },
];
Now I want to call map function on usersList and return a modified list like so :
var returnList = usersList.map((_user) => {
var _age;
try {
_age = _user.age;
} catch (error) {
console.log('I caught error here : ', error); // <-- not printed
_age = 'FAILSAFE-VAULE'; // <-- not set
}
var obj = {
firstName: _user.firstName,
lastName: _user.lastName,
age: _age
}
return obj;
});
I have a try-catch block inside the map function, the purpose if it is to replace the undefined property "age" of the second user with a 'FAILSAFE-VALUE'. but it doesn't work as should.
console.log(returnList);
// prints
// [
// { firstName: 'Adam', lastName: 'Yousif', age: 23 },
// { firstName: 'Mohamed', lastName: 'Ali', age: undefined }, <-- not what I want
// { firstName: 'Mona', lastName: 'Ahmed', age: 19 }
// ]
How to catch an error inside javascript map function?
Thank you
You don't need to do try catch for that:
usersList.map((_user) => {
return {
firstName: _user.firstName,
lastName: _user.lastName,
age: _user.age || 'FAILSAFE-VAULE'
};
});
That's because nothing is thrown (the age is just undefined). If you want information about this "error" during the map-operation, the first part of the snippet may be an idea. If you really want to use try - catch, use the second part (it throws 'manually' when _user.age is undefined). The latter demonstrates (by the way) that try - catch does work within a map-operation.
const usersList = [{
firstName: 'Adam',
lastName: 'Yousif',
age: 23
},
{
firstName: 'Mohamed',
lastName: 'Ali'
},
{
firstName: 'Mona',
lastName: 'Ahmed',
age: 19
},
];
const getFailSafe = user => {
if (!user.age) {
console.log(`Note: user.age not available for ${user.firstName} ${user.lastName}`);
return `FAILSAFE-VAULE`;
}
return user.age;
};
// 1. use a warning if .age not present
const returnList = usersList
.map((_user) => ({
firstName: _user.firstName,
lastName: _user.lastName,
age: getFailSafe(_user)
})
);
// 2. throw an Error if .age not present
const returnListWithError = usersList
.map((_user) => {
let retVal = {
firstName: _user.firstName,
lastName: _user.lastName,
}
try {
retVal.age = _user.age ||
(() => {
throw new Error(`ERROR: user.age not available for ${
_user.firstName} ${_user.lastName} (will continue with 'FAILSAFE-VAULE')`);
})();
} catch (err) {
console.log(`${err.message}`);
retVal.age = `FAILSAFE-VAULE`;
}
return retVal;
});
console.log(returnList.find(v => isNaN(v.age)));
console.log(returnListWithError.find(v => isNaN(v.age)));
.as-console-wrapper { top: 0; max-height: 100% !important; }
try...catch block would not work there as after accessing a value which does not exist you just receive an undefined
just do it next way:
var returnList = usersList.map((_user) => {
return {
firstName: _user.firstName,
lastName: _user.lastName,
age: _user.age ? _user.age : 'FAILSAFE-VAULE'
}
});

Categories

Resources