P.Yntema
P.Yntema

Reputation: 607

Javascript recursive function to change object structure

I want to convert an object structure to a structure which the Json-rules-engine can use.

I have this array as input, where A, B, C, D and E are some arbitrary condition.

INPUT

[
  {
    operator: 'any',
    conditions: [
      {
        operator: 'all',
        conditions: [
          { operator: 'all', conditions: [ A, B ] },
          { operator: 'any', conditions: [ C, D ] }
        ]
      },
      E
    ]
  }
]

I want to reach an output in the following structure:

{
    any: [E,
        {
            all: [{
                all: [A, B],
                any: [C, D]
            }],
        }
    ]
}

I am pretty sure that I need a recursive function for this. I have already tried the following. The problem here is that the output is overwritten, while I want it to be expanded when the recursive function reaches deeper levels of the array.

recursive(input, output) {
    input.forEach((el) => {
      if (typeof el !== "number") {
        output[el.operator] = el.conditions
        this.recursive(el.conditions, output);
      }
    });
  }

Thanks in advance!

Upvotes: 0

Views: 231

Answers (3)

Mulan
Mulan

Reputation: 135227

Adding my solution to the mix of other answers here. This transform does a simple type analysis on the input t and has excellent readability -

function transform (t)
{ switch (t?.constructor)
  { case Object:
      return { [t.operator]: transform(t.conditions) }
    case Array:
      return t.map(transform)
    default:
      return t
  }
}

const input =
  [{operator: 'any', conditions: [{operator: 'all', conditions: [{operator: 'all', conditions: ['A', 'B']}, {operator: 'any', conditions: ['C', 'D']}]}, 'E']}]

console.log(transform(input))

Because input is wrapped in an array, [...], the output is also wrapped -

[
  {
    "any": [
      {
        "all": [
          {
            "all": [
              "A",
              "B"
            ]
          },
          {
            "any": [
              "C",
              "D"
            ]
          }
        ]
      },
      "E"
    ]
  }
]

If you'd like to strip the outer array, you can either unwrap the input, transform(input[0]) or unwrap the output, transform(input)[0].

Upvotes: 2

Scott Sauyet
Scott Sauyet

Reputation: 50797

Here's a very simple function designed to transform one element of your input. As mentioned in the comments, I don't understand what the outer array in the input is for, and why there is no corresponding one in the output. But if you need to, you can just map this function over the input. (If they are supposed to fold into a single object, I think you need to explain how you would like that done.)

const transform = (x) => 
  Object (x) === x && 'operator' in x
    ? {[x .operator]: (x .conditions || []) .map (transform)}
    : x

const input = [{operator: 'any', conditions: [{operator: 'all', conditions: [{operator: 'all', conditions: ['A', 'B']}, {operator: 'any', conditions: ['C', 'D']}]}, 'E']}]

console .log (transform (input [0]))
.as-console-wrapper {max-height: 100% !important; top: 0}

It also does not change the condition order, which your output seems to want. (Because E comes first.) If you do want to reorder them, what's the requirement?

Upvotes: 2

Jamiec
Jamiec

Reputation: 136114

I split this into two methods as I think it reads a bit easier, but you could combine them if you wanted. Note that A,B etc I needed to make a string to make this runnable.

const convertObj = (obj) => {
  return {[obj.operator]: convertConditions(obj.conditions)}
}

const convertConditions = (arr) => {
  return arr.map(i => {
    if(typeof i == "string") {
      return i
    }else{
      return convertObj(i);
    }
  })
}

Live example:

var input = [{
  operator: 'any',
  conditions: [{
      operator: 'all',
      conditions: [{
          operator: 'all',
          conditions: ["A", "B"]
        },
        {
          operator: 'any',
          conditions: ["C", "D"]
        }
      ]
    },
    "E"
  ]
}]

const convertObj = (obj) => {
  return {[obj.operator]: convertConditions(obj.conditions)}
}

const convertConditions = (arr) => {
  return arr.map(i => {
    if(typeof i == "string") {
      return i
    }else{
      return convertObj(i);
    }
  })
}

var result = convertObj(input[0]);

console.log(result);

Upvotes: 1

Related Questions