我想创建一个从类型定义(作为纯字符串)中提取“泛型类型参数”的函数。
它应该像这样输入字符串:
Foo<Bar, Baz<Qux>>
并返回具有引用类型+泛型的对象,就像这样(当然,不必严格按照这种确切的格式,只要我可以检索所需的信息即可):
{
"name": "Foo",
"generics": [
{
"name": "Bar",
"generics": []
},
{
"name": "Baz",
"generics": [
{
"name": "Qux",
"generics": []
}
]
}
]
}
我的猜测是将String.match
与正则表达式一起使用,例如/<.*>/g
,用逗号分隔结果作为定界符,然后递归解析每个参数的泛型。但是,我觉得这太复杂了,我想念的还有一种更简单的方法。
答案 0 :(得分:1)
最简单的方法是递归构建键映射结构,然后将其转换为树。
下面的keyMapToTree
函数使用称为keyMapToTreeInner
的内部帮助函数。
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": {}
}
}
}
-->
答案 1 :(得分:0)
如果您是Chrome用户,则此代码可在控制台中运行:
SELECT DISTINCT ?pl
?pl_label
?abstract
?_thumbnail
( group_concat ( DISTINCT ?_influenced_label ; separator="; " ) AS ?influenced )
( group_concat ( DISTINCT ?_influencedBy_label ; separator="; " ) AS ?influencedBy )
( group_concat ( DISTINCT ?_sameAs ; separator=", " ) AS ?sameAs )
( group_concat ( DISTINCT ?_paradigm_label ; separator=", " ) AS ?paradigm )
WHERE
{
?pl rdf:type dbo:ProgrammingLanguage ;
rdfs:label ?pl_label
FILTER ( LANG ( ?pl_label ) = 'en' ) .
OPTIONAL { ?pl dbo:abstract ?abstract .
FILTER ( LANG ( ?abstract ) = 'en' ) . }
OPTIONAL { ?pl dbo:influenced ?_influenced .
?_influenced rdfs:label ?_influenced_label .
FILTER ( LANG ( ?_influenced_label ) = 'en' ) . }
OPTIONAL { ?pl dbo:influencedBy ?_influencedBy .
?_influencedBy rdfs:label ?_influencedBy_label
FILTER ( LANG ( ?_influencedBy_label ) = 'en' ) . }
OPTIONAL { ?pl owl:sameAs ?_sameAs . }
OPTIONAL { ?pl dbp:paradigm ?_paradigm .
?_paradigm rdfs:label ?_paradigm_label
FILTER ( LANG ( ?_paradigm_label ) = 'en' ) . }
OPTIONAL { ?pl dbo:thumbnail ?_thumbnail . }
}
GROUP BY ?pl ?pl_label ?abstract ?_thumbnail
答案 2 :(得分:0)
在Polywhirl先生的回答的启发下,我创建了以下实现:
(为清楚起见,使用Typescript type annotations)
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": []
}
]
}
]
}
与Polywhirl先生相比,我更喜欢这种实现方式,因为它可以立即创建正确的数据格式,而不需要其他转换步骤。 (我认为)这使它成为一种更清洁,更精简的解决方案。