Мой вопрос заключается в следующем. Я пытаюсь создать реагирующий элемент с помощью R, и я в основном хочу, чтобы панель поиска могла выполнять глобальный поиск нескольких строк, разделенных пробелом; так, например, я хочу иметь возможность вставлять в строку поиска следующее: «FOO BAR» и иметь возможность получать все строки, содержащие (в каждом порядке и в каждом столбце) как FOO, так и BAR; термины не обязательно должны находиться в одном столбце, но должны быть в одной строке.
Я изо всех сил пытаюсь это сделать; Я следую нескольким примерам в Интернете и нашел это в Интернете: https://github.com/glin/reactable/issues/222, в котором пользователь предлагает метод (см. код ниже из этого вопроса на github), который действительно работает хорошо. объединение разных строк с помощью OR.
library(reactable)
data <- as.data.frame(datasets::Titanic)
myFilterMethod <- JS('function(rows, columnIds, filterValue) {
/*
pattern defines a RegEx text based on the users input.
In this users input, all occurences of spacebar are replaced by an OR sign.
This is done so that if at least one his keywords is true, the row is displayed
However, if the expression ends with an OR sign, the OR sign should be removed.
If this is not done, then expressions ending in a spacebar will end in an OR sign
and will always give true. This is what the second replace does.
*/
const pattern = new RegExp(filterValue.replace(/ /g, "|").replace(/\\|$/, ""))
return rows.filter(function(row) {
return columnIds.some(function(columnId) {
return pattern.test(row.values[columnId])
// Use the pattern defined above to test the rows
// and return those that pass the patternn
})
})
}')
reactable(
data, searchable = TRUE,
searchMethod = myFilterMethod)
Как я могу создать аналогичную вещь, но объединить строку с помощью AND вместо OR?
Следующая идея должна сработать:
) на токены.Функция Javascript reduce
здесь ваш друг:
library(reactable)
data <- as.data.frame(datasets::Titanic)
searchForMultipleStrings <- JS("
function(rows, columnIds, filterValue) {
// tokens is an array of all search words
const tokens = filterValue.split(' ');
const result = tokens.reduce(
function (filtered_rows, token) {
return filtered_rows.filter(function (row) {
// need to transform object to array
const vals = Object.keys(row.values).map((key) => row.values[key]);
// for each entry in the row look if there is at least one single match
return vals.some((value) => value == token)
});
},
rows
)
return result;
}
")
reactable(
data, searchable = TRUE,
searchMethod = searchForMultipleStrings)
Н.Б. Я искал точные совпадения здесь (==
). Если вам нужна гибкость в использовании какого-то частичного соответствия, вы можете включить regex
в функцию .some()
.
Чтобы обеспечить «живой» поиск без учета регистра, адаптируйте код следующим образом:
searchForMultipleStrings <- JS("
function(rows, columnIds, filterValue) {
// tokens is an array of all search words
const tokens = filterValue.split(' ');
const result = tokens.reduce(
function (filtered_rows, token) {
return filtered_rows.filter(function (row) {
// need to transform object to array
const vals = Object.keys(row.values).map((key) => row.values[key]);
// for each entry in the row look if there is at least one single match
const re = new RegExp('^' + token + '\\S*', 'i');
return vals.some((value) => re.test(value))
});
},
rows
)
return result;
}
")
Это позволяет обеспечить частичное совпадение с начала строки. То есть строка поиска ma ch y 3
будет соответствовать Male | Child | Yes | 3rd
. Он нечувствителен к регистру. В зависимости от ваших конечных потребностей вам может потребоваться дополнительная адаптация регулярного выражения.
Обновил мой ответ запрошенной функцией.
Подход, аналогичный вашему, с использованием regex
поиска: определяется массив значений фильтра. Затем мы перебираем этот массив в цикле для получения логических значений, которые указывают на совпадение в каком-то столбце, и, наконец, every()
используется для объединения его в одно логическое значение.
library(reactable)
myFilterMethod <- JS('
function(rows, columnIds, filterValue) {
let arrFilterValues = filterValue.split(" ");
return rows.filter(function(row) {
return arrFilterValues.map(function(e) {
return columnIds.some(function(columnId) {
return new RegExp(e).test(row.values[columnId])
});
}).every(v => v === true);
});
}')
reactable(
as.data.frame(datasets::Titanic), searchable=TRUE,
searchMethod=myFilterMethod)
Огромное спасибо, это сработало - по сравнению со "стандартной" реагирующей формой поиска она кажется чувствительной к регистру - как можно сделать ее нечувствительной к регистру (т. е. в примере "мужчина" должен получать те же результаты, что и "мужчина" ")? Я полагаю, что можно применить filterValue.toLowerCase(filterValue.split(" ")) к предыдущему коду, но, похоже, это не работает.
Будьте осторожны с нечувствительностью к регистру в этом подходе. male
будет соответствовать как Female
, так и Male
, в зависимости от того, чего вы хотите, его может быть нежелательным.
@thothal, это тоже может быть желательно, и мне любопытно, как сделать этот пример нечувствительным к регистру, поскольку я также пытаюсь немного выучить язык JS. Есть идеи?
@thothal Я так не думаю. Если я буду искать male
, то получу только записи Female
, используя приведенный выше код ...
@Jan - видимо, я сам узнал - просто используйте return new RegExp(e,"i").test(row.values[columnId])
в вашем примере, чтобы сделать код нечувствительным к регистру. Спасибо! Подумайте о том, чтобы включить это в свой ответ, чтобы люди могли получить полную картину, если это необходимо.
@jan, в твоей текущей реализации это правда. Однако как только вы добавите нечувствительность к регистру, вы попадете в эту ловушку.
Спасибо! Ваше решение тоже работает. По сравнению с другим один отличается тем, что одному нужно ввести строку, которая точно соответствует строке, указанной в таблице, в то время как другой имеет преимущество своего рода «живого поиска». Что предпочтительнее, зависит от индивидуальных потребностей. Спасибо за вашу помощь.