У меня есть текстовый документ, представленный в виде array
предложений, и для каждого предложения у меня есть array
токенов слов.
Я должен вычислить абсолютное начало и конец позиции токена в документе для каждой позиции токена, следовательно, если в предложении у меня есть ipsum
пять раз, я должен получить правильную позицию в этом предложении для каждого вхождения .
Я написал эту функцию
// calculate begin and end to each token in a sentence
function calculateTokenBeginEnd(textArray) {
var currentText = [];
textArray.sentences.forEach(function(sentence) {
for (var i = 0; i < sentence.tokens.length; ++i) {
var token = sentence.tokens[i];
var word = token.word;
if (i > 0) {
var thisBegin = token.characterOffsetBegin;
var previousEnd = sentence.tokens[i - 1].characterOffsetEnd;
if (thisBegin > previousEnd) {
currentText.push(' ');
}
}
token.characterOffsetBegin = currentText.length;
for (var j = 0; j < word.length; ++j) {
currentText.push(word[j]);
}
token.characterOffsetEnd = currentText.length;
}
currentText.push('\n');
});
return textArray;
} //calculateTokenBeginEnd
но что-то не так. Рассчитанные characterOffsetBegin
и characterOffsetEnd
неверны.
Документ имеет следующую структуру
{
"sentences": [
{
"index": 0,
"text": "Lorem ipsum dolor sit amet,",
"tokens": [
{
"index": 1,
"word": "Lorem",
"characterOffsetBegin": 0,
"characterOffsetEnd": 5
},
{
"index": 2,
"word": "ipsum",
"characterOffsetBegin": 5,
"characterOffsetEnd": 10
},
...
]
},
{
"index": 1,
"text": " consectetur adipiscing elit,",
"tokens": [
{
"index": 1,
"word": "",
"characterOffsetBegin": 24,
"characterOffsetEnd": 24
},
...
}
Это пример использования этого метода. Затем calculateTokenBeginEnd
должен вычислить начальный и конечный индексы токена, тогда как text2SentencesTokens
создал структуру документа выше. calculateTokenBeginEnd
не работает должным образом.
text = "Lorem ipsum dolor sit amet,\n consectetur adipiscing elit,\nsed do eiusmod tempor incididunt\nut labore et dolore magna aliqua.\nUt enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi\nut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nLorem ipsum dolor sit amet etwas,\nsunt in culpa qui officia deserunt mollit anim id est laborum"
// to map a text to sentences and tokens
text2SentencesTokens = function(text) {
var self = this;
return new Promise((resolve, _) => {
let sentences = text.split(/\n+/g);
let sentencesP = sentences.map((sentence, lineIndex) => { // for each sentence
return new Promise((resolve, _) => {
let tokens = sentence.split(/\s+/g);
let tokensP = tokens.map((token, tokenIndex) => { // for each token
let item = {
"index": (tokenIndex + 1),
"word": token
}
if (typeof(tokenP) == 'function') {
return tokenP.apply(self, [item]);
} else {
return new Promise((resolve, _) => {
resolve(item);
});
}
});
Promise.all(tokensP)
.then(res => {
resolve({
index: lineIndex,
text: sentence,
tokens: res
});
})
.catch(err => console.error(err))
});
});
Promise.all(sentencesP)
.then(res => {
resolve({
sentences: res
})
})
.catch(err => console.error(err))
});
} //text2SentencesTokens
// calculate begin and end to each token in a sentence
function calculateTokenBeginEnd(textArray) {
var currentText = [];
textArray.sentences.forEach(function(sentence) {
for (var i = 0; i < sentence.tokens.length; ++i) {
var token = sentence.tokens[i];
var word = token.word;
if (i > 0) {
var thisBegin = token.characterOffsetBegin;
var previousEnd = sentence.tokens[i - 1].characterOffsetEnd;
if (thisBegin > previousEnd) {
currentText.push(' ');
}
}
token.characterOffsetBegin = currentText.length;
for (var j = 0; j < word.length; ++j) {
currentText.push(word[j]);
}
token.characterOffsetEnd = currentText.length;
}
currentText.push('\n');
});
return textArray;
} //calculateTokenBeginEnd
text2SentencesTokens(text)
.then(sentences => {
sentences = calculateTokenBeginEnd(sentences);
console.log(sentences);
})
[UPDATE]
Согласно предложению я переписал функцию следующим образом:
function calculateTokenBeginEnd(textArray) {
var wordStart=-1;
for (var j = 0; j < textArray.sentences.length; ++j) {
var sentence=textArray.sentences[j];
wordStart +=1;
for (var i = 0; i < sentence.tokens.length; ++i) {
var token = sentence.tokens[i];
var word = token.word;
var wordRegex = new RegExp("\\b(" + word + ")\\b", "gi");
var match = wordRegex.exec(sentence.text);
var previousEnd = 0;
wordStart += match.index + previousEnd;
var wordEnd = wordStart + word.length - 1;
token.characterOffsetBegin = wordStart;
token.characterOffsetEnd = wordEnd;
}
}
}//calculateTokenBeginEnd
Есть ли лучшее решение для этого?
[ОБНОВЛЕНИЕ 2]
Я обновил text2SentencesTokens
в соответствии с предлагаемым решением. Проблема заключается в том, что это решение не будет работать должным образом, когда в одном или нескольких предложениях будет несколько совпадений с одинаковым token
, поскольку оно перезапишет начальную и конечную позиции последней совпавшей позицией, поэтому токен down
здесь будет получить последние подходящие позиции:
{
"index": 2,
"word": "down",
"characterOffsetBegin": 70,
"characterOffsetEnd": 73
}
в первом появлении первого предложения, в то время как оно должно было иметь первую совпавшую позицию.
// convert a text document into a sentences array and a token array for each sentence
function text2SentencesTokens(text, tokenP) {
var self = this;
return new Promise((resolve, _) => {
let sentences = text.split(/\n+/g);
let sentencesP = sentences.map((sentence, lineIndex) => { // for each sentence
return new Promise((resolve, _) => {
let tokens = sentence.replace(/[\\+;:\?!\»\«\>\<\]\[\)\(,\.\‘'“”"]/g, '').split(/\s+/g);
let tokensP = tokens.map((token, tokenIndex) => { // for each token
let item = {
"index": (tokenIndex + 1),
"word": token
}
var escaped = token.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
var wordRegex = RegExp("\\b(" + escaped + ")\\b", "g");
var match = null;
while ((match = wordRegex.exec(text)) !== null) {
var wordStart = match.index;
var wordEnd = wordStart + token.length - 1;
item.characterOffsetBegin = wordStart;
item.characterOffsetEnd = wordEnd;
}
if (typeof(tokenP) == 'function') {
return tokenP.apply(self, [item, sentence]);
} else {
return new Promise((resolve, _) => {
resolve(item);
});
}
});
Promise.all(tokensP)
.then(res => {
resolve({
index: lineIndex,
text: sentence,
tokens: res
});
})
.catch(err => console.error(err))
});
});
Promise.all(sentencesP)
.then(res => {
resolve({
sentences: res
})
})
.catch(err => console.error(err))
});
} //text2SentencesTokens
text = "Steve down walks warily down the street down\nWith the brim pulled way down low";
text2SentencesTokens(text)
.then(res => console.log(JSON.stringify(res, null, 2)))