Validate when array contains specific value - joi

I want to use an rule like, but I'm having some issues to fullfill the "is" rule for the when condition on Joi validation library.
let schema = {
field1: Joi.array().items(Joi.string().valid('v1', 'v2')),
field2: Joi.when("field1", {
is: // if field1 contains at least 'v1',
then: Joi.object().keys(...),
otherwise: Joi.forbidden()
}
}

You can use array.items by listing all allowed types. If a given type is .required() then there must be a matching item in the array:
joi API reference
let schema = {
field1: Joi.array().items(Joi.string().valid('v1', 'v2')),
field2: Joi.when("field1", {
is: Joi.array().items(Joi.string().valid('v1').required(), Joi.string().valid('v2'))
then: Joi.object().keys(...),
otherwise: Joi.forbidden()
}
}

Related

`find_one` does not give ObjectId in proper format

As the title states, here is the following code.
let users_coll = db
.database("foo")
.collection::<bson::oid::ObjectId>("users");
let user_id = users_coll
.find_one(
doc! { "email": &account.email },
mongodb::options::FindOneOptions::builder()
.projection(doc! { "_id": 1i32 })
.build(),
)
.await?
.unwrap();
But it fails at ? operator with the following mongodb::error::Error,
Error { kind: BsonDeserialization(DeserializationError { message: "expected map containing extended-JSON formatted ObjectId, instead found { \"_id\": ObjectId(\"62af199df4a16d3ea6056536\") }" }), labels: {}, wire_version: None, source: None }
And it is right. Given ObjectId should be in this format,
{
"_id": {
"$oid": "62af199df4a16d3ea6056536"
}
}
But I do not know how to handle this. Any help is appreciated.
Have a good day!
Your users collection isn't a collection of ObjectIds, it's actually a collection of documents which each contain an ObjectId. To let Rust know what to do with those, you should create a struct which represents the document, or at least the parts which you care about getting back from your query, and tell your collection to de-serialize into that struct:
use mongodb::bson::oid::ObjectId;
use serde::{Serialize, Deserialize};
#[derive(Debug, Default, Serialize, Deserialize)]
struct User {
_id: ObjectId,
}
#[tokio::main]
async fn main() {
let users_coll = db
.database("foo")
.collection::<User>("users");
let user_id: ObjectId = users_coll
.find_one(
doc! { "email": &account.email },
mongodb::options::FindOneOptions::builder()
.projection(doc! { "_id": 1i32 })
.build(),
)
.await?
.unwrap()
._id;
}
By default, the BSON fields have to match the struct fields exactly (_id in this case), but I'm pretty sure serde has a way to change that if you don't like the leading underscore.

Sort an array of objects, following the order of another array that has a value of the objects of the first array

How could I sort an array of objects taking into account another array?
Example:
let sortArray = [90, 1000, 4520]
let notSortArrayObject = [ { id: 4520, value: 4 }, {id: 1000, value: 2}, {id: 90, value:10} ]
So I want an array equal notSortArrayObject but sortered by sortArray value
let sortArrayObject =[{id: 90, value:10} , {id: 1000, value: 2}, { id: 4520, value: 4 }]
Here's an approach. For each number in the "guide" array, find a match in the not sorted and add it to the sortedObjectArray. Note that this ignores any values which don't have matches in the other array.
var sortedObjectArray: [Object] = [] // whatever your object type is
for num in sortArray {
if let match = notSortArrayObject.first(where: {$0.id == num}) {
sortedObjectArray.append(match)
}
}
If you just want to sort by the id in ascending order, it's much simpler:
let sorted = notSortArrayObject.sorted(by: {$0.id < $1.id})
I noticed that sortArray is already sorted - you could just sort notSortArrayObject by id, but I assume that's not the point of what you're looking for.
So, let's say that sortArray = [1000, 4520, 90] (random order), to make things more interesting.
You can iterate through sortArray and append the values that match the id to sortArrayObject.
// To store the result
var sortArrayObject = [MyObject]()
// Iterate through the elements that contain the required sequence
sortArray.forEach { element in
// Add only the objects (only one) that match the element
sortArrayObject += notSortArrayObject.filter { $0.id == element }
}

Coffeescript convert Array to Dict where the dict will have multiple values

In coffeescript, I'm trying to convert an array of objects into a dict, where one of the values of the object is taken as the key and all of the objects in the array with that value and up as being in an array in the dict linked to that key.
I have tried the code suggested here but this results in maximum one object per key. https://coffeescript-cookbook.github.io/chapters/arrays/creating-a-dictionary-object-from-an-array
I couldn't find any other examples that don't just result in one value per key.
So, for example (expanding on the example linked above), I have an array
cats = [
{
name: "Bubbles"
age: 1
},
{
name: "Sparkle"
favoriteFood: "tuna"
age: 2
},
{
name: "Felix"
age: 2
}
]
I want my result to be
catDict = {
1: [
{
name: "Bubbles"
age: 1
}
]
2: [
{
name: "Sparkle"
favoriteFood: "tuna"
age: 2
},
{
name: "Felix"
age: 2
}
]
}
catDict = {}
(catDict[cat.age]?.push(cat) or catDict[cat.age] = [cat]) for cat in cats
I used the accessor variant of the existential operator ?. to soak up null references. When a null reference is encountered the second half of the or kicks in to initialize the array.
It's shorter, but I'm not sure if it's more elegant...
Classic, coming up with a solution immediately after I give up and post the question on StackOverflow, but here's my solution:
addCatToDict = (cat, dict) ->
key = cat.age
if key of dict then dict[key].push(cat)
else dict[key] = [cat]
catDict = {}
for cat in cats
addCatToDict(cat, catDict)
interested to see more elegant solutions

Using Array Attribute Type in Sails

I'm looking to use the sails attribute type 'array' in my app, but I can't find documentation for this anywhere.
I'd like to do the following:
module.exports = {
attributes : {
images: {
type: ['string']
},
location: {
type: ['float','float']
}
}
}
image is an array that will hold a list of image urls and location will hold 2 floats. Will this work in sail's? Else how can I get this to work.
Thanks
PS: I'm working solely with MongoDB
As of Sails 1.0 type array is no longer supported.
The type "array" is no longer supported. To use this type in your model, change
type to one of the supported types and set the columnType property to a column
type supported by the model's adapter, e.g. { type: 'json', columnType: 'array' }
SOLUTION ONE
Set up property to store an images array and a location array...
module.exports = {
attributes : {
images: {
type: 'json',
columnType: 'array'
}
location: {
type: 'json',
columnType: 'array'
}
}
}
SOLUTION TWO
A more elegant solution is to set up a single object to store both filename and location data
module.exports = {
attributes : {
images: {
type: 'json'
}
}
}
Then in your controller you would store object properties as arrays...
let imageData = {
filename: ["image1.jpg", "image2.jpg", "image3.jpg"],
location: [154.123123, 155.3434234, 35.12312312]
};
Images.create({images:imageData});
Some issues when storing data to the json object is that a string like "image1.jpg,image2.jpg,image3.jpg" will store in MongoDb no worries... doh. Ensure that when POSTing you may need to split the data .split(',').
sailsjs provide a function to solve your question,you can try this
module.exports = {
attributes : {
images: {
type: 'string'
},
location: {
type: 'json'
}
}
}
As far as I know, you can only specify it like this:
module.exports = {
attributes : {
images: {
type: 'array'
},
location: {
type: 'array'
}
}
}
See Sails ORM Attributes
For sails 1.0, for array maybe you can try this way i'm using just for sharing.
Also you can replace before update and process native query() and delete the attributes for updating by waterline. Hope this help you.
variants:
{
type: 'json',
columnType: 'array',
custom: (value) =>
{
/*
[
code : unique, string
name : string, maxLength[30]
cost : number, isFinite
price : number, isFinite
default : boolean
]
*/
return _.isArray(value)
&& _.every(value, (variant1) =>
{
return _.countBy(value, (variant2) =>
{
return variant2.code == variant1.code ? 'eq' : 'uneq';
}).eq <= 1
&& _.isString(variant1.name) && variant1.name.length < 30
&& _.isNumber(variant1.cost) && _.isFinite(variant1.cost)
&& _.isNumber(variant1.price) && _.isFinite(variant1.price)
&& _.isBoolean(variant1.default);
});
},
},
You can use type as ref for arrays and objects.
module.exports = {
attributes: {
images: {
type: 'ref'
},
location: {
type: 'ref'
}
}
}

Swift dictionary all containing

Lets say I have dictionaries like below and wanted an array of red dogs. I figured I need to get an array of all the names of the type "dog" using the first dictionary, and then use the name key and the color to search the final dictionary to get ["Polly,"jake"]. I've tried using loops but can't figure out how to iterate through the dictionary.
var pets = ["Polly" : "dog", "Joey" : "goldfish", "Alex" : "goldfish", "jake" : "dog"]
var petcolor = ["Polly" : "red", "Joey" : "black", "Alex" : "yellow", "jake":red"]
The correct solution would seem to be to create a Pet struct (or class) and collate all of this information into a struct and build either an array or dictionary full of these values.
struct Pet {
let name: String
let type: String
let color: String
init(name: String, type: String, color: String) {
self.name = name
self.type = type
self.color = color
}
}
Now, let's build an array of these pets:
var goodPets = [Pet]()
for (petName, petType) in pets {
guard let petColor = petcolor[petName] else {
// Found this pet's type, but couldn't find its color. Can't add it.
continue
}
goodPets.append(Pet(name: petName, type: petType, color: petColor))
}
Now that we've filled out goodPets, pulling out any particular subset of Pets becomes very easy:
let redDogs = goodPets.filter { $0.type == "dog" && $0.color = "red" }
And although this answer looks like a lot of set up & legwork compared to other answers, the major advantage here is that once we build the goodPets array, any way we want to scoop pets out of there ends up being more efficient. And as we increase the number of properties the pets have, this becomes more and more true compared to the other answers.
If you'd rather store our model objects in a dictionary continuing to use the names as the keys, we can do that as well, but the filter looks a little bit stranger.
Building the dictionary looks mostly the same:
var goodPets = [String : Pet]()
for (petName, petType) in pets {
guard let petColor = petcolor[petName] else {
// Found this pet's type, but couldn't find its color. Can't add it.
continue
}
goodPets[petName] = (Pet(name: petName, type: petType, color: petColor))
}
But the filter is slightly different:
let redDogs = goodPets.filter { $0.1.type = "dog" && $0.1.color = "red" }
Note that in both cases, redDogs has the type [Pet], that is, an array of Pet values.
You can iterate through a dictionary like this:
for key in pets.keys() {
if pets[key] == "Dog" {
}
}
Or:
for (name, pet) in pets {
if pet == "Dog" {
}
}
nhgrif is probably correct about structure but, to answer the literal question:
let dogs = Set(pets.filter { $0.1 == "dog" }.map { $0.0 })
let redAnimals = Set(petscolor.filter { $0.1 == "red" }.map { $0.0 })
let redDogs = dogs.intersect(redAnimals)
Each filter is a block that operates on a (key, value) tuple, testing the value and ultimately creating a dictionary with only the matching (key, value) pairs. Each map then converts that filtered dictionary into an array by discarding the values and just keeping the keys.
Each array is turned into a set to support the intersect operation. The intersect then determines the intersection of the two results.