1

I've looked through Joi APIs but there's no such thing as array order. I also looked into Joi refs but currently it's impossible (correct me if I'm wrong) to use them inside arrays.

I'm thinking of using extend but not sure it's possible to retrieve the whole array.

Input:

const asc = [1,2,3];
const noOrder = [10,7,8];
const desc = [6,5,4];

Desired output:

Joi.validate(asc, Joi.array().asc()) // True
Joi.validate(asc, Joi.array().desc()) // False
Joi.validate(desc, Joi.array().desc()) // False
Joi.validate(noOrder, Joi.array().desc()) // False
Joi.validate(noOrder, Joi.array().asc()) // True

So my question is, how do I get started with this? Any idea is greatly appreciated

Brian Le
  • 2,646
  • 18
  • 28

2 Answers2

1

Joi doesn't offer any built-in way to validate the order of an array so you will have to extend with your own extension, like so:

const Joi = require('joi');

const customJoi = Joi.extend((joi) => ({
  base: joi.array(),
  name: 'array',
  language: {
      asc: 'needs to be sorted in ascending order',
      desc: 'needs to be sorted in descending order'
  },

  rules: [
      {
          name: 'asc',        
          validate(params, value, state, options) { 
            const isAscOrder = value.every((x, i) => i === 0 || x >= value[i - 1]);
            return isAscOrder ? value : this.createError('array.asc', {v: value}, state, options);             
          }
      },
      {
          name: 'desc',          
          validate(params, value, state, options) {
            const isDescOrder = value.every((x, i) => i === 0 || x <= value[i - 1]);
            return isDescOrder ? value : this.createError('array.desc', {v: value}, state, options);             
          }
      }
  ]
}));

const ascSchema = customJoi.array().asc();
const descSchema = customJoi.array().desc();

// Validation results.
console.log(Joi.validate([5, 7, 9, 10], ascSchema)); //true
console.log('\n\n');
console.log(Joi.validate([5, 7, 6, 10], ascSchema)); //false
console.log('\n\n');
console.log(Joi.validate([5, 4, 2, 0], descSchema)); //true
console.log('\n\n');
console.log(Joi.validate([5, 4, 2, 6], descSchema)); //false
f-CJ
  • 4,235
  • 2
  • 30
  • 28
  • Hello and thanks for the answer. Quick question would it be more performant to use `forEach()` then `createError` than `every`? Or even a `for` loop? – Brian Le Apr 13 '19 at 13:10
  • You are welcome. `Every` method will stop checking array elements once it finds one element that does not satisfy the condition. You can achieve the same behavior with a `foreach` and `for` loop but you will have to explicitly break the loop, so the solution will be much more verbose. Another good thing about `every` is that the function passed is usually a pure function with no side effects. You can check more about the difference between each array method here: https://stackoverflow.com/questions/7340893/what-is-the-difference-between-map-every-and-foreach – f-CJ Apr 13 '19 at 13:20
  • Thanks for the useful link. Accepted the answer – Brian Le Apr 13 '19 at 13:23
  • 1
    Cheers @BrianLe! See you around. – f-CJ Apr 13 '19 at 13:29
1

As of v16.0.0, Joi supports validating the order of an array with the following schema:

Joi.array()
    .items(Joi.number())
    .sort({ order: 'ascending' });
followben
  • 9,067
  • 4
  • 40
  • 42