Извлечь параметры универсального типа из строки - PullRequest
0 голосов
/ 27 марта 2019

Я хочу создать функцию, которая извлекает «параметры универсального типа» из определения типа (в виде простой строки).

Должна быть строка ввода, подобная этой:

Foo<Bar, Baz<Qux>>

И вернуть объект с указанными типами + обобщенные элементы, что-то вроде этого (конечно, не обязательно в этом точном формате, если я могу получить необходимую информацию):

{
   "name": "Foo",
   "generics": [
      {
         "name": "Bar",
         "generics": []
      },

      {
         "name": "Baz",
         "generics": [
            {
               "name": "Qux",
               "generics": []
            }
         ]
      }
   ]
}

Полагаю, можно использовать String.match с таким регулярным выражением, как /<.*>/g, разделить результат с запятой в качестве разделителя и рекурсивно проанализировать обобщения для каждого параметра. Тем не менее, я чувствую, что это слишком сложно, и что я упускаю гораздо более простой способ.

Ответы [ 3 ]

1 голос
/ 27 марта 2019

Самый простой способ сделать это - рекурсивно построить структуру карты ключей и затем преобразовать ее в дерево.

В приведенной ниже функции keyMapToTree используется внутренняя вспомогательная функция с именем keyMapToTreeInner. * 1005.*

console.log(keyMapToTree(parseAsKeyMap('Foo<Bar, Baz<Qux>>')));

function parseAsKeyMap(input, tree = {}) {
  input = input.trim();
  let startIndex = input.indexOf('<'),
    endIndex   = input.lastIndexOf('>');
  if (startIndex !== -1 && endIndex === -1) {
    throw new Error("Missing closing bracket '>' for " + input);
  } else if (startIndex === -1 && endIndex !== -1) {
    throw new Error("Missing opening bracket '<' for " + input);
  } else if (startIndex !== -1 && endIndex !== -1) {
    let head = input.substring(0, startIndex),
      tail = input.substring(startIndex + 1, endIndex);
    tree[head] = {};
    tail.split(/\s*,\s*/).forEach(token => parseAsKeyMap(token, tree[head]));
  } else {
    tree[input] = {};
  }
  return tree;
}

function keyMapToTree(input) {
  let keys = Object.keys(input);
  if (keys.length !== 1) {
    throw new Error('Object must be non-null and have only one key!');
  }
  let key = keys[0], node = { name: key, generics: [] };
  keyMapToTreeInner(input[key], node.generics);
  return node;
}

function keyMapToTreeInner(input, nodeArray) {
  Object.keys(input).map(key => {
    let node = { name: key, generics: [] };
    keyMapToTreeInner(input[key], node.generics);
    nodeArray.push(node)
  });
}
.as-console-wrapper {
  top: 0;
  max-height: 100% !important;
}
<!--

The initial key-map will look like this, so convert this structure to a tree.

{
  "Foo": {
    "Bar": {},
    "Baz": {
      "Qux": {}
    }
  }
}

-->
0 голосов
/ 27 марта 2019

Сильно вдохновленный ответом г-на Поливирла, я создал следующую реализацию:

примечаниями типа машинописного текста для ясности)

type TypeInfo = { //the returned object format
    name: string;
    generics: TypeInfo[];
}

function parseGenerics(input: string): TypeInfo {
    input = input.trim();
    const startIndex = input.indexOf('<'),
          endIndex = input.lastIndexOf('>');

    if (startIndex !== -1 && endIndex === -1) {
        throw new Error("Missing closing bracket '>' for " + input);
    } else if (startIndex === -1 && endIndex !== -1) {
        throw new Error("Missing opening bracket '<' for " + input);
    } else if (startIndex === -1 && endIndex === -1) { //no generics
        return {
            name: input,
            generics: []
        };
    } else {
        const head = input.substring(0, startIndex),
              tail = input.substring(startIndex + 1, endIndex);

        return {
            name: head,
            generics: tail.split(/\s*,\s*/).map(parseGenerics)
        };
    }
}

Использование Foo<Bar, Baz<Qux>> в качестве входных данных приводит к:

{
    "name": "Foo",
    "generics": [
        {
            "name": "Bar",
            "generics": []
        },
        {
            "name": "Baz",
            "generics": [
                {
                    "name": "Qux",
                    "generics": []
                }
            ]
        }
    ]
}

Я предпочитаю эту реализацию г-ну Поливирлу, потому что она сразу создает правильный формат данных вместо необходимости дополнительных шагов преобразования.Это делает его (по моему мнению) более чистым и более рациональным решением.

0 голосов
/ 27 марта 2019

если вы пользователь Chrome, этот код работает в консоли:

// let inputString = "Foo<Bar, Baz<Qux<Some, Thing<Else<But, Not>, So<Is>>, Other>>>"
let inputString = "Foo<Bar, Baz<Qux>>"
const replacements = {}
let replacementIndex = 0
while (true) {
  const replacement = (inputString.match(/[A-Z][a-z0-9]+<(([A-Z][a-z0-9]+)[,\s]*)+>/) || [])[0]
  if (replacement) {
    const key = `Key${replacementIndex}`
    replacementIndex++
    replacements[key] = replacement
    inputString = inputString.replace(replacement, key)
  } else {
    break
  }
}

const resultJson = {}

const parseSentence = (text) => {
  const [key, valuesStr] = text.replace(/>$/, '').split(/</)
  const values = valuesStr.split(',').map((x) => x.trim())
  return {
    [key]: values,
  }
}

Object.keys(replacements).forEach((key) => {
  resultJson[key] = parseSentence(replacements[key])
})

while (true) {
  let replacementsFound = false
  Object.keys(resultJson).forEach((key) => {
    Object.keys(resultJson[key]).forEach((name) => {
      resultJson[key][name] = resultJson[key][name].map((value) => {
        if (/^Key[\d+]$/.test(value)) {
          replacementsFound = true
          return resultJson[value]
        }
        return value
      })
    })
  })
  if (!replacementsFound) {
    break
  }
}

const resultKey = `Key${replacementIndex - 1}`
const unpreparedResult = resultJson[resultKey]

const prepareResultJson = (json) => {
  const name = Object.keys(json)[0]
  const generics = []
  json[name].forEach((generic) => {
    if (typeof generic === 'string') {
      generics.push({ name: generic, generics: [] })
    } else {
      generics.push(prepareResultJson(generic))
    }
  })
  const result = {
    name,
    generics,
  }
  return result
}

const finalResult = prepareResultJson(unpreparedResult)

console.log(finalResult)

Также вы можете следовать этому URL: https://codepen.io/SergioBelevskij/pen/ZPdVyM

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...