JS使用点、字符串表示法映射嵌套和赋值错误

JS using dot, string notation to map nested and assign errors

好吧,这是一个奇怪的问题,我似乎无法正确理解。

我有一个大而复杂的对象要发送到后端。经过各种尝试,我尝试使用 Joi 来验证模式,我喜欢它,但是将错误传回输入是一场噩梦

主体分为5个部分,每个部分包含10-30个字段,其中一些是string[]interface[]number[]或一般的嵌套接口。

我尝试编写自己的自定义验证,但复杂性变得无法控制。

(我知道你们中的一些人在想“你们的模式太复杂了”,你是对的,但这不是我现在可以改变的。客户等等等等。)

问题:Joi.validate(myBody) 给我一堆错误,格式如下:

[ // <- error.details
  {
    context: {},
    message: "X is not allowed to be empty",
    path:["path","to","property"], // <- this is the key
    type: ""
  }
]

如何映射 error.details 以创建一个新的验证对象,然后我可以将其用于表单项本身。

例如:

path = ["path","to","property"] // -> map over to create

let newObj = {
  path:{
    to: {
       property: ""
    }
}
}

我希望这是有道理的。

我想获取一组验证错误,并将它们转换为与初始对象匹配的验证对象

IMO 最简单的方法是使用 reduce 从数组反向创建对象

["path","to","property"].reduceRight((prev, current) => {
  const obj = {};
  
  obj[current] = prev
  
  return obj;
}, "");

这将创建原始问题中描述的对象。您需要使用 reduceRight 而不是 reduce 以便首先创建叶节点,否则每次添加新节点时都必须尝试遍历图形并且必须处理设置叶节点成为一个字符串而不是一个对象。


推断出您要实现的目标我假设有几件事:

  • 您想 return 单个对象而不是对象数组。
  • 叶子很可能就是消息。
  • 每条路径只会有一个错误消息(因为它使生活更轻松)。

我们可以用 deep merge solution from here 扩展上面的内容来创建一个对象到 return。该代码如下所示:

const errors = [ // <- error.details
  {
    context: {},
    message: "X is not allowed to be empty",
    path:["path","to","property"], // <- this is the key
    type: ""
  },
   {
    context: {},
    message: "X has to be greater than 0",
    path:["path","to","another", "property"], // <- this is the key
    type: ""
  }
]

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }

  return mergeDeep(target, ...sources);
}

errors.map((e) => e.path.reduceRight((prev, current) => {
  const obj = {};
  
  obj[current] = prev
  
  return obj;
}, e.message)).reduce((previous, current) => mergeDeep(previous, current), {})

运行 errors 通过它的输出将是:

{
  path: {
    to: {
      property: 'X is not allowed to be empty',
      another: { property: 'X has to be greater than 0' }
    }
  }
}

正在使用的问题陈述

我想获取一组验证错误,并将它们转换为与初始对象匹配的验证对象

给定:一个字符串数组(每一个都是一个prop)

预期结果:基于数组构造的对象

代码段

// given an array of props 
// starting at root-level & ending at the leaf
const pList = ['root', 'lev1', 'lev2', 'propName'];

// method to transform array into an object
const transformToObj = arr => (
  arr
  .reverse()
  .reduce(
    (fin, itm) => ({ [itm]: fin ? {...fin} : '' }),
    false
  )
);

// test with given array
console.log(transformToObj(pList));

// test with user-input array (comma-separated)
console.log(transformToObj([...(
  (prompt('Enter comma-separated path like a,b,c,d'))
  .split(',')
  .map(x => x.trim())
)]));

说明

  • 首先反转数组(所以第一项是inner-most属性)
  • 使用.reduce迭代
  • 在每个级别,将项目添加为 outer-prop,并将值添加为现有对象
  • 如果这是 inner-most 属性,只需添加一个空字符串作为值