Joi - validate property against value in array of objects - javascript

Im trying to validate a value of a particular property in a payload against a defined array of objects.
for example
payload
{
a:[1, 2]
}
the value of "a" must be a one of the id defined in a array of objects (multiple values allowed)
[
{
"id":1,
"share":{
"x":100,
"y":0,
"z":0
}
},
{
"id":2,
"share":{
"x":90,
"y":0,
"z":10
}
}
....and so on
]
Could you please help advising if this can be achieved with Joi?
Thanks, Gowrish

Joi's array.validate() with items should do what you're looking for.
const Joi = require("joi")
const input = { a: [ 1, 2 ] }
const objects = [{ id: 1 }, { id: 2 }, { id: 3 }]
const schema = {
a: Joi.array().items(Joi.any().valid(objects.map(o => o.id)))
}
const result = Joi.validate(input, schema, (err, result) => {
if (err) {
console.error('error:', err)
return
}
console.log('result:', result)
})

Related

MongoDB find value in array that does not exist in database

Here I have an array. Some of the values listed below already exist in MongoDB, but I need the values that do not currently exist in the database
ex: -
Values currently in the database
[
{"id":100},
{"id":500},
{"id":606},
{"id":800}
]
the value I have
let x = [100,300,400,500,606,800];
I need the output to consist of the following values:
300,400
These values need to be added because they do not already exist in the database
dbo.collection.aggregate([{
$group: {
_id: null,
names: {
"$addToSet": "$uid"
}
}
},
{
"$replaceRoot": {
"newRoot": {
results: {
$filter: {
input: x,
as: "datum",
cond: {
$not: {
"$setIsSubset": [
[
"$$datum"
],
"$names"
]
}
}
}
}
}
}
}
]).toArray(function(err, result) {
console.log(result);
})

Return variable from database find instead of array

I need to pull two values out of my mongoDB database and right now the code is returning an array. How do I get:
totalGuests
attendedGuests
out of the query and stored in variables so I can display them on the client?
module.exports.showEvent = async(req, res,) => {
const event = await Event.findById(req.params.id).populate('artist');
if (!event) {
req.flash('error', 'Cannot find that Event');
return res.redirect('/events');
}
res.render('events/show', { event });
const { guest_id } = req.cookies;
const lookUp = Event.collection.find({ _id: req.params.id},
{
_id: 1,
name: 1,
guests: 1,
totalGuests: {
$size: "$guests"
},
attendedGuests: {
$size: {
"$filter": {
input: "$guests",
cond: {
$eq: [
"$$this.attended",
"Y"
]
}
}
}
}
});
console.log(lookUp);

array some is not working in nested array

I want to check if current user is present in nested array or not.
this is few part of my sample array which is getting from API:
[
{
"owner":"abc",
"_id":"xyz77",
"comments":[
],
"likes":[
{
"_id":"9999",
"username":"user1"
},
{
"_id":"9998",
"username":"user2"
}
]
},
{
"owner":"bcd"
}
]
I want to see if user1 is present in likes array or not.
if yes then it should give output like this:
[
{
"owner":"abc",
"user1":true
},
{
"owner":"bcd",
"user1":true
},
{
"owner":"def",
"user1":false
}
]
above result is likes array of owner abc has user1 but not present in owner def.
I tried with array.some for likes array inside forEach of owner array. But not getting proper result.
help is appreciated
You can use a combination of Array.prototype.map and Array.prototype.some to create a resulting array which checks if any of the users in the likes array of each owner object matches your username:
const data = [
{
"owner":"abc",
"_id":"xyz77",
"comments":[],
"likes":[
{
"_id":"9999",
"username":"user1"
},
{
"_id":"9998",
"username":"user2"
}
]
},
{
"owner":"bcd",
"_id":"xyz88",
"comments":[],
"likes":[
{
"_id":"9998",
"username":"user2"
},
{
"_id":"9997",
"username":"user3"
}
]
},
];
const checkUsername = (data, username) => {
return data.map(e => {
const x = { owner: e.owner };
x[username] = e.likes.some(el => el.username === username);
return x;
});
};
console.log(checkUsername(data, 'user1'));
console.log(checkUsername(data, 'user2'));
Its Similar to #awarrier99 answer, Use destructuring along with map and some.
const data = [
{
owner: "abc",
_id: "xyz77",
comments: [],
likes: [
{
_id: "9999",
username: "user1",
},
{
_id: "9998",
username: "user2",
},
],
},
{
owner: "bcd",
},
];
const update = (arr, user) =>
data.map(({ likes = [], owner }) => ({
[user]: likes.some(({ username }) => username === user),
owner,
}));
console.log(update(data, "user1"));

How to access an object nested inside an array by the value of one of its keys? [duplicate]

This question already has answers here:
Filtering object properties based on value
(8 answers)
Closed 3 years ago.
Say I have an object that looks like this:
{
"data": {
"postsConnection": {
"groupBy": {
"author": [
{
"key": "xyz",
"connection": {
"aggregate": {
"count": 5
}
}
},
{
"key": "abc",
"connection": {
"aggregate": {
"count": 3
}
}
}
]
}
}
}
}
How would one access the value of count corresponding to the author element that has, say, xyz as its key? I know for this particular example I could just do this:
const n = data.postsConnection.groupBy.author[0].connection.aggregate.count
But that would mean knowing in advance which element in the array holds the desired value for key, which isn't the case in my context.
If the author can appear multiple times, you can .filter() the array stored at author and then .map() the results to the count:
const data = {data:{postsConnection:{groupBy:{author:[{key:"xyz",connection:{aggregate:{count:5}}},{key:"abc",connection:{aggregate:{count:3}}}]}}}};
const author = "xyz";
const res = data.data.postsConnection.groupBy.author.filter(({key}) => key === author).map(obj => obj.connection.aggregate.count);
console.log(res);
// If you want to get the total of all counts for the given author, you can use reduce on the result to sum:
const totalCountsForAuthor = res.reduce((acc, n) => acc+n, 0);
console.log(totalCountsForAuthor);
If the author can only appear once, you can use .find() instead of .filter() like so:
const data = {data:{postsConnection:{groupBy:{author:[{key:"xyz",connection:{aggregate:{count:5}}},{key:"abc",connection:{aggregate:{count:3}}}]}}}};
const author = "xyz";
const res = data.data.postsConnection.groupBy.author.find(({key}) => key === author).connection.aggregate.count
console.log(res);
You can use Array#find to get the first instance inside an array that meets a certain condition (in your case, the first instance whose key value is equal to the key value you want).
var obj = {"data":{"postsConnection":{"groupBy":{"author":[{"key":"xyz","connection":{"aggregate":{"count":5}}},{"key":"abc","connection":{"aggregate":{"count":3}}}]}}}};
function getAuthorByKey(key) {
return obj.data.postsConnection.groupBy.author.find(author => author.key === key);
}
console.log(getAuthorByKey("xyz").connection.aggregate.count);
console.log(getAuthorByKey("abc").connection.aggregate.count);
If the author array always exists:
const data = {
postsConnection: {
groupBy: {
author: [{
key: "xyz",
connection: {
aggregate: {
count: 5
}
}
}, {
key: "abc",
connection: {
aggregate: {
count: 3
}
}
}]
}
}
};
function getCount(keyVal) {
const element = data.postsConnection.groupBy.author.find(item => item.key === keyVal)
return element.connection.aggregate.count || "";
}
console.log(getCount('xyz'))
var data = { "data": {
"postsConnection": {
"groupBy": {
"author": [
{
"key": "xyz",
"connection": {
"aggregate": {
"count": 5
}
}
},
{
"key": "abc",
"connection": {
"aggregate": {
"count": 3
}
}
}
]
}
}
}
};
data.data.postsConnection.groupBy.author.forEach((autor) => {
if(autor.key === "xyz")
console.log(autor.connection.aggregate);
});
You can make use of array find, to find the author by "key".
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
const author = data.postsConnection.groupBy.author.find((author) => author.key === "xyz")
CodePen:
https://codepen.io/gmaslic/pen/wvwbLoL
Then you can access all the properties from found "author"

Searching JSON deeply nested Objects with dynamic keys using Lodash?

I have a nested JSON that I'd like to search through using lodash. How can I get the root object from data if a search term I'm looking for is within certain keys, and with one of the keys being dynamic?
For example, if I have:
"data": [
{
"name": "Bob's concourse"
"activities": [
{
"day": "Monday",
"routines":
{
"Biking":
{
"details": "won 3 trophies"
"type": "road"
},
"Kayaking":
{
"details": "participated in 3 races"
"type": "rhythm"
}
}
}
}
]
},
{..other_data_etc...},
]
activities can be []; it's not guaranteed that it contains any data.
routines keys are dynamic. ie, Biking, Kayaking are dynamic strings. It can be anything.
If I want to search for an races (case insensitive), I want to search specifically in:
data.name
data.activities.routines.* (the dynamic keys)
data.activities.routines.*.details
If any one of those matches, then it will return the root object: { "name": "Bob", ..... }
I was able to get the name to return:
function searchText(collection, searchterm) {
return _.filter(collection, function(o) {
return _.includes(o.name.toLowerCase(), searchterm)
} );
};
But I'm still new to lodash, and I was unable to get any of the nested searches to return correctly, especially with the dynamic keys part.
Could anyone help explain a solution?
Expanding on your existing attempt with lodash:
const obj = {
data: [{
name: 'Bob\'s concourse',
activities: [{
day: 'Monday',
routines: {
Biking: {
details: 'won 3 trophies',
type: 'road'
},
Kayaking: {
details: 'participated in 3 races',
type: 'rhythm'
}
}
}]
}]
};
function search(str, data) {
const searchStr = str.toLowerCase();
// Only return the entries that contain a matched value
return _.filter(data, (datum) => {
// Check if name matches
return _.includes(datum.name, searchStr)
|| _.some(datum.activities, (activity) => {
return _.entries(activity.routines).some(([routine, {details}]) => {
// Check if dynamic routine matches or details
return _.includes(routine, searchStr) || _.includes(details, searchStr);
});
});
});
}
console.log(search('foobar', obj.data));
console.log(search('races', obj.data));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.10/lodash.min.js"></script>
You can also accomplish this with plain JavaScript. Using some newish syntax such as destructuring assignment and newish native methods such as Object.entries essentially follows the same pattern as using lodash:
const obj = {
data: [{
name: 'Bob\'s concourse',
activities: [{
day: 'Monday',
routines: {
Biking: {
details: 'won 3 trophies',
type: 'road'
},
Kayaking: {
details: 'participated in 3 races',
type: 'rhythm'
}
}
}]
}]
};
function search(str, data) {
const regex = RegExp(str, 'i');
// Only return the entries that contain a matched value
return data.filter((datum) => {
// Check if name matches
return regex.test(datum.name)
|| datum.activities.some((activity) => {
return Object.entries(activity.routines).some(([routine, {details}]) => {
// Check if dynamic routine matches or details
return regex.test(routine) || regex.test(details);
});
});
});
}
console.log(search('foobar', obj.data));
console.log(search('races', obj.data));

Categories