0

I'm using Zod to validate the input of my application forms and when a validation error happens I receive an array of errors with a message property and a path property.

I need to transform the received path property to a string object path so I can use it to create a ValidationError for React Final Form.

Given path:

["user", "name"]
["company", 0, "name"]

Expected string object path:

"user.name"
"company[0].name"

Surprisingly I didn't find any code on Stack Overflow, Google Search or NPM that implements this :)

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Alfonso
  • 1,125
  • 1
  • 13
  • 23
  • 1
    Seems like a pretty simple loop would work. Skip the first value; if the current value is a string, add a `.` and append the current value. If the current value is a number, add a `[`, the current value, and `]`. Continue through the array. – Heretic Monkey Nov 04 '20 at 20:53
  • 2
    It would be much easier if you converted those to `user['name']` and `company['0']['name']` - less (no) edge cases to handle. – zerkms Nov 04 '20 at 20:55
  • 1
    Yeah, I'd worry about more edge cases here. Any string can be a property key, even ones which are not valid JS identifiers. – jcalz Nov 04 '20 at 21:18
  • @jcalz sorry for commenting to you directly, but I think you might be interested to see https://stackoverflow.com/q/64676046/251311 – zerkms Nov 04 '20 at 22:51

3 Answers3

2

A quick attempt:

const string_path = (path) =>
  path.reduce(
    (acc, item) => acc + (Number.isInteger(item) ? '[' + item + ']' : '.' + item),
    ''
  ).substring(1);

console.log(string_path(["user", "name"]));
console.log(string_path(["company", 0, "name"]));

Edit: So, from looking at @vich's post I learned that reduce will happily use the first element of the array as the accumulator if you don't specify one, so here's a slightly shorter version:

const string_path = (path) =>
  path.reduce(
    (acc, item) => acc + (Number.isInteger(item) ? '[' + item + ']' : '.' + item)
  );

console.log(string_path(["user", "name"]));
console.log(string_path(["company", 0, "name"]));
console.log(string_path(["user"]));
Ben Stephens
  • 3,303
  • 1
  • 4
  • 8
1

You can use Array.reduce to achieve what you want.

const paths = ["company", 0, "name"];

const result = paths.reduce((acc, item) => {
  return typeof item === "string" ?
    acc + "." + item :
    `${acc}[${item}]`

}, "");

console.log(result.slice(1));
Eldar
  • 9,781
  • 2
  • 10
  • 35
1

This is quite trivial. It was worth making your own attempt.

["user", "name"].reduce((string, item) => (typeof item === "number") ? string + "[" + item + "]" : string + "." + item)

"user.name"

["company", 0, "name"].reduce((string, item) => (typeof item === "number") ? string + "[" + item + "]" : string + "." + item)

"company[0].name"

vich
  • 262
  • 2
  • 15
  • Thanks for your answer! yeah it was quite trivial but I am not sure about all the edge cases so that's why I finally decided to ask to SO. Anyways I decided to go for the approach that @zerkms commented as it makes me more confident about the code. – Alfonso Nov 04 '20 at 21:54