import { isNumber, stripHTML } from './utils'; import CellManager from './cellmanager'; export default function filterRows(rows, filters) { let filteredRowIndices = []; if (Object.keys(filters).length === 0) { return rows.map(row => row.meta.rowIndex); } for (let colIndex in filters) { const keyword = filters[colIndex]; const filteredRows = filteredRowIndices.length ? filteredRowIndices.map(i => rows[i]) : rows; const cells = filteredRows.map(row => row[colIndex]); let filter = guessFilter(keyword); let filterMethod = getFilterMethod(rows, filter); if (filterMethod) { filteredRowIndices = filterMethod(filter.text, cells); } else { filteredRowIndices = cells.map(cell => cell.rowIndex); } } return filteredRowIndices; }; function getFilterMethod(rows, filter) { const getFormattedValue = cell => { let formatter = CellManager.getCustomCellFormatter(cell); if (formatter && cell.content) { cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rows[cell.rowIndex]); return stripHTML(cell.html); } return cell.content || ''; }; const stringCompareValue = cell => String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase(); const numberCompareValue = cell => parseFloat(cell.content); const getCompareValues = (cell, keyword) => { if (cell.column.compareValue) { const compareValues = cell.column.compareValue(cell, keyword); if (compareValues && Array.isArray(compareValues)) return compareValues; } // check if it can be converted to number const float = numberCompareValue(cell); if (!isNaN(float)) { return [float, keyword]; } return [stringCompareValue(cell), keyword]; }; let filterMethodMap = { contains(keyword, cells) { return cells .filter(cell => { const hay = stringCompareValue(cell); const needle = (keyword || '').toLowerCase(); return !needle || hay.includes(needle); }) .map(cell => cell.rowIndex); }, greaterThan(keyword, cells) { return cells .filter(cell => { const [compareValue, keywordValue] = getCompareValues(cell, keyword); return compareValue > keywordValue; }) .map(cell => cell.rowIndex); }, lessThan(keyword, cells) { return cells .filter(cell => { const [compareValue, keywordValue] = getCompareValues(cell, keyword); return compareValue < keywordValue; }) .map(cell => cell.rowIndex); }, equals(keyword, cells) { return cells .filter(cell => { const value = parseFloat(cell.content); return value === keyword; }) .map(cell => cell.rowIndex); }, notEquals(keyword, cells) { return cells .filter(cell => { const value = parseFloat(cell.content); return value !== keyword; }) .map(cell => cell.rowIndex); }, range(rangeValues, cells) { return cells .filter(cell => { const values1 = getCompareValues(cell, rangeValues[0]); const values2 = getCompareValues(cell, rangeValues[1]); const value = values1[0]; return value >= values1[1] && value <= values2[1]; }) .map(cell => cell.rowIndex); }, containsNumber(keyword, cells) { return cells .filter(cell => { let number = parseFloat(keyword, 10); let string = keyword; let hayNumber = numberCompareValue(cell); let hayString = stringCompareValue(cell); return number === hayNumber || hayString.includes(string); }) .map(cell => cell.rowIndex); } }; return filterMethodMap[filter.type]; } function guessFilter(keyword = '') { if (keyword.length === 0) return {}; let compareString = keyword; if (['>', '<', '='].includes(compareString[0])) { compareString = keyword.slice(1); } else if (compareString.startsWith('!=')) { compareString = keyword.slice(2); } if (keyword.startsWith('>')) { if (compareString) { return { type: 'greaterThan', text: compareString.trim() }; } } if (keyword.startsWith('<')) { if (compareString) { return { type: 'lessThan', text: compareString.trim() }; } } if (keyword.startsWith('=')) { if (isNumber(compareString)) { return { type: 'equals', text: Number(keyword.slice(1).trim()) }; } } if (isNumber(compareString)) { return { type: 'containsNumber', text: compareString }; } if (keyword.startsWith('!=')) { if (isNumber(compareString)) { return { type: 'notEquals', text: Number(keyword.slice(2).trim()) }; } } if (keyword.split(':').length === 2) { compareString = keyword.split(':'); return { type: 'range', text: compareString.map(v => v.trim()) }; } return { type: 'contains', text: compareString.toLowerCase() }; }