Joi validate array of objects with when condition - express

First of all, sorry for bad English.
I can't find any docs about this.
WHAT I WANT TO DO
const docs = {
type: 'a', // ['a',' 'b', 'c'] is available.
items: [
{
a: 123,
b: 100 // => This value only available when type is 'a' or 'b'. otherwise, forbidden.
}
]
};
MY JOI SCHEMA(NOT WORKED)
Joi.object({
type: Joi.string().valid('a', 'b', 'c').required(),
items: Joi.array()
.items(
Joi.object({
a: Joi.number().required()
b: Joi.number()
})
)
.when('type', {
is: Joi.string().valid('a', 'b'),
then: Joi.array().items(Joi.object({ b: Joi.number().required() })),
otherwise: Joi.array().items(Joi.object({ b: Joi.number().forbidden() }))
})
})
This code is not working correctly.
When type is 'c', it validate passed.
How can I fix this?

You have added .items() to items: Joi.array() which overrides the .when() condition, try using
Joi.object({
type: Joi.string().valid('a', 'b', 'c').required(),
items: Joi.array()
.when('type', {
is: Joi.string().valid('a', 'b'),
then: Joi.array().items(Joi.object({
a: Joi.number().required(),
b: Joi.number().required()
})),
otherwise: Joi.array().items(Joi.object({
a: Joi.number().required()
}))
})
})
example

Related

Nest js: How to received a dto field with array of predefined strings

I have a dto which is called product and it has a field called units....which received array of strings and this strings are predefined.....
my valid strings are predefined in a array ...
let validItems = ['a', 'b', 'c', 'd', 'e']
the data I want to be accepted by my dto is ...
{
product_id: 1,
units: ['a', 'b', 'c']
}
{
product_id: 2,
units: ['c', 'e', 'd']
}
{
product_id: 3,
units: ['e', 'b', 'a']
}
my current dto(not requirement satisfied) is =>
export class Product {
#IsString({ message: 'Product id must be a string' })
product_id: string;
#IsArray({ message: 'unit must be array' })
#IsString({ each: true, message: 'must be a string' })
units: string[];
}
what will be my DTO in nest.js. As I am new to nest.js so kindly provide me some good docs for this decorators
😛 I tried a lot and found a way to solve this problem.
I spent quite some time on the swagger docs for an array of strings.
import { ApiProperty } from "#nestjs/swagger";
import { IsArray, IsString } from "class-validator";
export class MenuOptionsByNames {
#ApiProperty({ isArray: true, example: ["size_external_party"] })
#IsArray()
#IsString({ each: true, message: "Each item should be string" })
menu_names: Array<string>;
}

Ramda reduce from array to object?

How can I switch from array to object data type in pipeline using ramda's reduce in point-free style?
I would like to achieve this:
(nodes) => nodes.reduce((acc, node: any) => {
acc[node.id] = {
out: node.outgoing_explicit,
in: node.incoming_explicit
};
return acc;
}, {})
Index the nodes by id, and the map them and use R.applySpec to change to the in/out format:
const { pipe, indexBy, map, applySpec, prop } = R
const fn = pipe(
indexBy(prop('id')),
map(applySpec({
out: prop('outgoing_explicit'),
in: prop('incoming_explicit'),
}))
)
const nodes = [{ id: 1, outgoing_explicit: 'abc1', incoming_explicit: 'xyz1' }, { id: 2, outgoing_explicit: 'abc2', incoming_explicit: 'xyz2' }, { id: 3, outgoing_explicit: 'abc3', incoming_explicit: 'xyz3' }]
const result = fn(nodes)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
this could also be a solution:
const spec = {
in: R.prop('incoming_explicit'),
out: R.prop('outgoing_explicit'),
}
const fn = R.reduceBy(
R.flip(R.applySpec(spec)),
null,
R.prop('id'),
);
const data = [
{ id: 'a', incoming_explicit: 'Hello', outgoing_explicit: 'World' },
{ id: 'b', incoming_explicit: 'Hello', outgoing_explicit: 'Galaxy' },
{ id: 'c', incoming_explicit: 'Hello', outgoing_explicit: 'Universe' },
{ id: 'd', incoming_explicit: 'Hello', outgoing_explicit: 'Dimension' },
];
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>
The solutions involving applySpec are probably best, but just for variety, here's an alternative:
const convert = pipe (
map (juxt ([prop('id'), props(['incoming_explicit', 'outgoing_explicit'])])),
fromPairs,
map (zipObj (['in', 'out']))
)
const nodes = [{id: 'foo', outgoing_explicit: 43, incoming_explicit: 42}, {id: 'bar', outgoing_explicit: 20, incoming_explicit: 10}, {id: 'baz', outgoing_explicit: 309, incoming_explicit: 8675}]
console .log (convert (nodes))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script>const {pipe, map, juxt, prop, props, fromPairs, zipObj} = R </script>
`juxt' is a bit of an oddball function. It works like this:
juxt([f, g, h, ...]) //=> (a, b, ...) -> [f(a, b, ...), g(a, b, ...), h(a, b, ...), ...]

Vue - how to add reactive properties to objects in an array of objects?

I have an array of objects coming from the backend. I need to add additional data onto each object, to send.
My data coming in looks like this:
let arr = [
{
id: 1,
city: 'Orlando',
},
{
id: 2,
city: 'Miami',
},
{
id: 3,
city: 'Portland',
}
]
When the data comes loaded in through Vue, I need to have those properties be reactive. Meaning vue can see when those values change and call the appropiate lifecycle methods. See https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
let newArr = [
{
id: 1,
city: 'Orlando',
state: 'Fl',
country: 'USA',
},
{
id: 2,
city: 'Miami',
state: 'Fl',
country: 'USA',
},
{
id: 3,
city: 'Portland',
state: 'OR',
country: 'USA',
}
]
You can't just loop through and append properties to each object normally in vanillaJS, so how would you do this?
The vue docs suggests to use Object.assign when appending properties to make them reactive
// instead of `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
But, this is only if you have to append one object.
For a list of objects, this is how you would initialize the data
newArr = arr.map(item => {
let newItem = Object.assign({}, item, {
state: undefined,
country: undefined,
});
return newItem;
});
Now all your objects properties in the array of objects are reactive
Vue also intercepts calls to Array.push, so you may like this better:
arr.forEach(item => newArr.push(item))
Quite late but thought of providing the answer as it can be useful to someone else in the future:
If you would like to add an additional property to a certain object within an array of objects then you can use something like this:
var searchId = 1
const cityInfo = arr.find(ele => ele.id === searchId)
Vue.set(identifiersNode, 'state', "Karnataka")
Vue.set(identifiersNode, 'country', "India")

Ramda: Is there a way to find particular key value is nested object?

I want to find particular key value is nested object or not.
{
'a': {
'area': 'abc'
},
'b': {
'area': {
'city': 'aaaa',
'state': 'ggggg'
}
}
}
In above example, I want to find 'a' and 'b' is object or nested object?
If you want to know whether all keys in the object contain nested objects then one possible solution is to convert all of the values of the object to boolean values using R.map and R.propSatisfies, representing whether the nested property was an object or not.
const fn = R.map(R.propSatisfies(R.is(Object), 'area'))
const example = {
'a': {
'area': 'abc'
},
'b': {
'area': {
'city': 'aaaa',
'state': 'ggggg'
}
}
}
console.log(fn(example))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
If you just want to know whether a specific key of an object contains a nested object, then you can do so with a composition of R.prop and R.propSatisfies.
const fn = R.pipe(R.prop, R.propSatisfies(R.is(Object), 'area'))
const example = {
'a': {
'area': 'abc'
},
'b': {
'area': {
'city': 'aaaa',
'state': 'ggggg'
}
}
}
console.log('a:', fn('a', example))
console.log('b:', fn('b', example))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Transform objects pointfree style with Ramda

Given the function below, how do I convert it to point-free style? Would be nice to use Ramda's prop and path and skip the data argument, but I just can't figure out the proper syntax.
const mapToOtherFormat = (data) => (
{
'Name': data.Name
'Email': data.User.Email,
'Foo': data.Foo[0].Bar
});
One option would be to make use of R.applySpec, which creates a new function that builds objects by applying the functions at each key of the supplied "spec" against the given arguments of the resulting function.
const mapToOtherFormat = R.applySpec({
Name: R.prop('Name'),
Email: R.path(['User', 'Email']),
Foo: R.path(['Foo', 0, 'Bar'])
})
const result = mapToOtherFormat({
Name: 'Bob',
User: { Email: 'bob#example.com' },
Foo: [{ Bar: 'moo' }, { Bar: 'baa' }]
})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
Here's my attempt:
const mapToOtherFormat = R.converge(
(...list) => R.pipe(...list)({}),
[
R.pipe(R.view(R.lensProp('Name')), R.set(R.lensProp('Name'))),
R.pipe(R.view(R.compose(R.lensProp('User'), R.lensProp('Email'))), R.set(R.lensProp('Email'))),
R.pipe(R.view(R.compose(R.lensProp('Foo'), R.lensIndex(0), R.lensProp('Bar'))), R.set(R.lensProp('Foo')))
]
)
const obj = {Name: 'name', User: {Email: 'email'}, Foo: [{Bar: 2}]}
mapToOtherFormat(obj)
Ramda console
[Edit]
We can make it completely point-free:
const mapToOtherFormat = R.converge(
R.pipe(R.pipe, R.flip(R.call)({})),
[
R.pipe(R.view(R.lensProp('Name')), R.set(R.lensProp('Name'))),
R.pipe(R.view(R.compose(R.lensProp('User'), R.lensProp('Email'))), R.set(R.lensProp('Email'))),
R.pipe(R.view(R.compose(R.lensProp('Foo'), R.lensIndex(0), R.lensProp('Bar'))), R.set(R.lensProp('Foo')))
]
)
Ramda console