您可以将此问题建立在相关问题的基础上,breakAt
它在多个切割点处分割字符串。这可以建立在将列表变成其连续对的列表的基础上。 (那是,[2, 3, 5, 7] => [[2, 3], [3, 5], [5, 7]]
.) 这两个函数都可以在您的应用程序中具有其他合理的用途。
这是一个以这种方式构建的实现:
const intoPairs = xs => xs.slice(1).map((x, i) => [xs[i], x])
const breakAt = (places, str) => intoPairs([0, ...places, str.length]).map(
([a, b]) => str.substring(a, b)
)
const getText = (offset, length, str) => breakAt([offset, offset+ length], str)
const str = "Do you have questions or comments and do you wish to contact ABC? Please visit our customer support page."
console.log(getText(83, 16, str))
我不太清楚您首选的输出格式是什么。这只是根据您的情况生成一个包含之前、之内和之后文本的数组offset
, length
, and str
.
Update
后续评论询问如何分解多个子字符串。这正是这种轻微概括的目的。该版本建立在breakAt
编写一个接受多个的新函数offset
/length
配对以便进一步分解单词。它不会尝试处理重叠的可能性;那将是呼叫者的监视。但确实如此,请对这些对进行排序,以便您不必按顺序提供它们。
const intoPairs = xs => xs.slice(1).map((x, i) => [xs[i], x])
const breakAt = (places, str) => intoPairs([0, ...places, str.length]).map(
([a, b]) => str.substring(a, b)
)
const breakWhere = (words, str) => breakAt(
words.slice(0).sort(({offset: o1}, {offset: o2}) => o1 - o2).reduce(
(a, {offset, length}) => [...a, offset, offset + length],
[]
),
str
)
const str = "Do you have questions or comments and do you wish to contact ABC? Please visit our customer support page."
console.log(breakWhere([
{offset: 83, length: 16}, // "customer support"
{offset: 12, length: 9}, // "questions"
{offset: 25, length: 8}, // "comments"
], str))
The slice
调用只是为了避免改变列表offset/length
对。如果不担心的话,你可以放弃它。
另一个更新
另一个后续评论(这个问题的最后一个,@stacks;下次开始一个新问题!)询问如何将输出格式化为跟踪纯文本与链接文本的节点。这是一个相当幼稚的版本:
const intoPairs = xs => xs.slice(1).map((x, i) => [xs[i], x])
const breakAt = (places, str) => intoPairs([0, ...places, str.length]).map(
([a, b]) => str.substring(a, b)
)
const breakWhere = (words, str) => breakAt(
words.reduce((a, {offset, length}) => [...a, offset, offset + length], []),
str
)
const createNodes = (links, str) => {
const sortedLinks = links.slice(0).sort(({offset: o1}, {offset: o2}) => o1 - o2)
return breakWhere(sortedLinks, str).map((s, i) => i % 2 == 0
? {data: s, type: 'text'}
: {data: s, type: 'link', path: sortedLinks[(i - 1) / 2].path}
).filter(({data}) => data.length > 0)
}
const str = "Do you have questions or comments and do you wish to contact ABC? Please visit our customer support page."
const links = [
// {offset: 0, length: 6, path: '/path/to/doYou'},
{offset: 83, length: 16, path: '/path/to/custSupport'},
{offset: 12, length: 9, path: 'path/to/questions'},
{offset: 25, length: 8, path: 'path/to/comments'},
]
console.log(createNodes(links, str))
这将早期调用的简化版本包装为将备用字符串映射到的版本type: 'text'
and type: 'link'
对象,每个对象都有一个data
财产。这些链接还给出了path
. That path
可能不适用于您的数据,您可以在代码中跳过它。
The filter
最后的调用是删除可能为空的文本节点。如果两个链接相邻,或者如果字符串的开头或结尾有一个链接,则将存在一个空节点。您可以通过取消注释额外的内容来看到这一点link
。如果您删除filter
调用,这将留下一个初始的空文本节点,可能不是我们想要的。
我称这个版本为幼稚的,因为它不处理多种可能性,最重要的是重叠部分。这似乎有点困难,而且对于您的项目来说可能永远不需要。
Note
这种方法的一个重要的事情是它layered。我们不是尝试编写一个一次性完成所需所有事情的函数,而是构建辅助函数。intoPairs
and breakAt
两者本身可能都有用。breakWhere
and createNodes
可能更具体地解决这个问题。我建造了createNodes
已经创建了breakWhere
。将新功能分层在旧功能之上非常简单。
换句话说,设计本身就包含了一些需求变化的历史。它在性能方面可能不是最佳的,不是因为有任何严重缺陷,而只是因为它最初创建时没有考虑到最终要求。有时值得回过头来重新思考这样的代码,但这并不总是重要的。您必须决定所使用的附加循环是否对您的应用程序来说是一个严重的问题,或者您是否只需要分层较少的代码。