Как хранить многоуровневые условия в массиве

Я хочу закодировать функцию для обработки определяемых условий.

Справочные данные содержатся в объекте, а простое условие хранится в массиве из 3 элементов, например:

["name", "= = ", "John Doe"]

вот код, который хорошо работает для проверки простого условия:

function getResultOfSimpleCondition(data, condition) {
    let c1 = data[condition[0]],
        operateur = condition[1],     
        c2 = condition[2], cond=true;
        switch(operateur){
                case "= = " :
                case " = "  :         cond = (c1 == c2 ); break;
                case "! = " :         cond = (c1 != c2 ); break;
                case ">"  :         cond = (c1 >  c2 ); break;
                case "<"  :         cond = (c1 <  c2 ); break;
                case "> = " :         cond = (c1 >= c2 ); break;
                case "< = " :         cond = (c1 <= c2 ); break;
                case "like":        cond = (c1.indexOf(c2) >  -1); break;
                case "not like":    cond = (c1.indexOf(c2) == -1); break;
                default   :         cond = (c1 == c2 ); break;
        }
    return cond
}

let myData = { name:'John Doe', age:'28', town:'PARIS', qty:5, uptodate: true},

    condition_0 = ["name", "= = ", "Jack Sparrow"],    // result false
    condition_1 = ["age", "> = ", "24"],               // result true
    condition_2 = ["uptodate", "= = ", false],         // result false
    condition_3 = ["town", "= = ", "PARIS"];           // result true

console.info( getResultOfSimpleCondition(myData, condition_0) )

я ищу, как реализовать более сложные условия по тому же принципу.

Например:

на 2 уровнях:

[ condition_0, "OR", condition_1 ] // результат истинный

или

[ condition_1, "AND", condition_2 ] // результат ложный

на большем количестве уровней:

[[ condition_0, "OR", condition_1 ], "AND", condition_3] // результат истинный

или

[[ condition_0, "OR", condition_1 ], "AND", condition_3, "AND NOT", [condition_5, "OR", condition_23 ] ]

код будет выглядеть

let myData = { name:'John Doe', age:'28', town:'PARIS', qty:5, uptodate: true},
    complexCondition = [[ condition_0, "OR", condition_1 ], "AND", condition_3, "AND NOT", [condition_5, "OR", condition_23 ] ];

function getResultOfComplexCondition(data, condition){
...
}

console.info( getResultOfComplexCondition(myData, complexCondition) )

заранее спасибо

так же, как и с функцией getResultOfSimpleCondition, я хочу иметь возможность решать более сложные логические выражения (условия). Я привел несколько примеров выше

Lionel KAISER 20.04.2023 20:34

Для «читаемого» решения здесь может работать рекурсия. Функция, которая проходит через каждый дочерний массив (или дочерний элемент дочернего элемента и т. д.) в сложных условиях, пока не найдет только строковые элементы, вызовет вашу функцию simpleConditions, а затем создаст эти результаты для резервного копирования дерева условий до верхнего уровня. Это, вероятно, не будет очень производительным — возможно, вы захотите изучить создание небольшого механизма правил.

Zac Anger 20.04.2023 20:55

Скорее всего, вы захотите реализовать свои " = " и "! = ", используя настоящие операторы javascript === и !== вместо == и !=, чтобы избежать ловушек, таких как 0 == false

Rodrigo Rodrigues 20.04.2023 21:58
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
3
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я немного упростил ваше выражение, но для демонстрации рекурсивного обхода AST (абстрактного синтаксического дерева). Я использовал Acorn для анализа упрощенной версии предоставленных вами выражений.

Что касается ваших «нравится» и «не нравится», вам нужно будет разработать собственную грамматику. См. «Абстрактные синтаксические деревья с парсером рекурсивного спуска» для логики токенизации с использованием регулярных выражений.

const evaluateExpression = (context, expression) => {
  const visitor = new NodeVisitor({ context });
  const ast = acorn.parse(expression, { ecmaVersion: '2020' });
  //console.info(ast);
  return visitor.visit(ast.body[0].expression);
};

const main = () => {
  const myData = { name: 'John Doe', age: 28, town: 'PARIS', qty: 5, uptodate: true };
  console.info(evaluateExpression(myData, 'age >= 24 && town == "PARIS"'));
};

// Adapted from:
// https://inspirnathan.com/posts/163-abstract-syntax-trees-with-recursive-descent-parser/
class NodeVisitor {
  constructor({ context }) {
    this.context = context;
  }

  visit(node) {
    switch (node.type) {
      case 'Literal':
        return this.visitLiteral(node);
      case 'Identifier':
        return this.visitIdentifier(node);
      case 'BinaryExpression':
        return this.visitBinaryExpression(node);
      case 'LogicalExpression':
        return this.visitLogicalExpression(node);
    }
  }

  visitLiteral(node) {
    return node.value;
  }

  visitIdentifier(node) {
    return node.name;
  }

  visitBinaryExpression(node) {
    switch (node.operator) {
      case '<':
        return this.context[this.visit(node.left)] < this.visit(node.right);
      case '<=':
        return this.context[this.visit(node.left)] <= this.visit(node.right);
      case '>':
        return this.context[this.visit(node.left)] > this.visit(node.right);
      case '>=':
        return this.context[this.visit(node.left)] >= this.visit(node.right);
      case '==':
        return this.context[this.visit(node.left)] === this.visit(node.right);
      case '!=':
        return this.context[this.visit(node.left)] !== this.visit(node.right);
      default:
        throw new Error(`Invalid operation: ${node.operator}`);
    }
  }

  visitLogicalExpression(node) {
    switch (node.operator) {
      case '&&':
        return this.visit(node.left) && this.visit(node.right);
      case '||':
        return this.visit(node.left) || this.visit(node.right);
      default:
        throw new Error(`Invalid operation: ${node.operator}`);
    }
  }
}

main();
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src = "https://cdnjs.cloudflare.com/ajax/libs/acorn/8.8.2/acorn.min.js"></script>
<!--
// Acorn AST of parsed expression:
{
  "type": "Program",
  "start": 0,
  "end": 28,
  "body": [
    {
      "type": "ExpressionStatement",
      "start": 0,
      "end": 28,
      "expression": {
        "type": "LogicalExpression",
        "start": 0,
        "end": 28,
        "left": {
          "type": "BinaryExpression",
          "start": 0,
          "end": 9,
          "left": {
            "type": "Identifier",
            "start": 0,
            "end": 3,
            "name": "age"
          },
          "operator": "> = ",
          "right": {
            "type": "Literal",
            "start": 7,
            "end": 9,
            "value": 24,
            "raw": "24"
          }
        },
        "operator": "&&",
        "right": {
          "type": "BinaryExpression",
          "start": 13,
          "end": 28,
          "left": {
            "type": "Identifier",
            "start": 13,
            "end": 17,
            "name": "town"
          },
          "operator": "= = ",
          "right": {
            "type": "Literal",
            "start": 21,
            "end": 28,
            "value": "PARIS",
            "raw": "\"PARIS\""
          }
        }
      }
    }
  ],
  "sourceType": "script"
}
-->

Другие вопросы по теме