JS 使用点、字符串表示法来映射嵌套和分配错误

发布于 2025-01-14 05:02:25 字数 845 浏览 1 评论 0原文

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

我有一个大而复杂的对象要发送到后端。经过各种尝试后,我尝试使用 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: ""
    }
}
}

我希望这是有道理的。

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

Ok, this is an odd one that I just can't seem to get right.

I have a large, complex object to send to a backend. After various attempts, I tried using Joi to validate the schema, and I like it, but passing the errors back to the inputs is a nightmare

The body is subdivided into 5 sections, with each subsection containing between 10-30 fields, some of which are string[], interface[], number[], or general nested interfaces.

I tried writing my own custom validation and the complexity grew outta control.

(I know some of you are thinking "your schema is too complex" and you're right, but its not something I can change right now. Clients blah blah.)

The problem: Joi.validate(myBody) gives me a bunch of errors in the following format:

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

How can I map error.details to create a new validation object that I can then use for the form items themselves.

For example:

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

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

I hope this make sense.

I want to take an array of vallidation errors, and turn them into a validation object that matches the initial object

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

纵山崖 2025-01-21 05:02:25

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

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

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


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

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

我们可以使用此处的深度合并解决方案来扩展上述内容 创建一个要返回的对象。其代码如下所示:

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' }
    }
  }
}

The simplest approach IMO would be to use reduce to reverse create the object from the array

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

This will create the object as described in the original question. You need to use reduceRight rather than reduce so that you create the leaf node first otherwise you have having to try and traverse the graph each time you add a new node and you have to handle setting the leaf node to be a string rather than an object.


Extrapolating out what you are trying to achieve I'm assuming a couple of things:

  • You want to return a single object rather than an array of objects.
  • The leaf is likely to be the message.
  • There will only be a single error message for each path (because it makes life easier).

We can expand upon the above with the deep merge solution from here to create an object to return. The code for that would look like:

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), {})

The output from running errors through it would be:

{
  path: {
    to: {
      property: 'X is not allowed to be empty',
      another: { property: 'X has to be greater than 0' }
    }
  }
}
廻憶裏菂餘溫 2025-01-21 05:02:25

正在使用的问题陈述

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

给定:字符串数组(每个字符串都是一个 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())
)]));

解释

  • 首先反转数组(因此第一项是最里面的 prop),
  • 使用 .reduce 在每个级别进行迭代
  • ,将该项添加为outer-prop,然后将value 作为现有对象
  • 如果这是最里面的 prop,只需添加一个空字符串作为 value

Problem statement being used

I want to take an array of vallidation errors, and turn them into a validation object that matches the initial object

Given: an array of strings (each of which is a prop)

Expected result: an object structured based on the array

Code Snippet

// 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())
)]));

Explanation

  • first reverse the array (so the first item is the inner-most prop)
  • use .reduce to iterate
  • at each level, add the item as the outer-prop and the value as the existing object
  • if this is the inner-most prop, simply add an empty string as value
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文