I am trying to filter an HTML table and show me the rows that exist that have ALL of the matches of my keywords -
For instance. I have my html table as follows
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0pky{border-color:inherit;text-align:left;vertical-align:top}
</style>
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0pky{border-color:inherit;text-align:left;vertical-align:top}
</style>
<table >
<thead>
<tr>
<th >ID</th>
<th >Name</th>
<th >Tags</th>
</tr>
</thead>
<tbody>
<tr>
<td >1</td>
<td >Test1</td>
<td >AIRPORT TERMINAL,AVIATION,HANGAR,MAINTENANCE BUILDING,NEW CONSTRUCTION,OFFICE</td>
</tr>
<tr>
<td >2</td>
<td >Test2</td>
<td >HIGHER EDUCATION,OFFICE,RENOVATION,SCHOOLS - COLLEGE</td>
</tr>
<tr>
<td >3</td>
<td >Test3</td>
<td >HIGHER EDUCATION,REMODEL,SCHOOLS - COLLEGE</td>
</tr>
<tr>
<td >4</td>
<td >Test4</td>
<td >APARTMENT,HIGHER EDUCATION,NEW CONSTRUCTION,SCHOOLS - COLLEGE</td>
</tr>
</tbody>
</table>
From here I am trying to do JQuery or JavaScript to filter and only show me the rows that contain atleast ALL of the specific words.
So for instance.
If I send an array
const Keywords = ["HIGHER EDUCATION", "RENOVATION"];
it should show me rows with ID 2 only If I send an array
const Keywords = ["HIGHER EDUCATION", "SCHOOLS-COLLEGE"];
it should show me rows with ID 2 and 3 as they both contain those words it should not show me 1 or 4 as it does not contain all those words plus extras.
I plan on using this that I currently use to filter my columns on other items -
var jo = $("#" tablename).find("tr:not(:first)");
jo.filter(function (i, v) {
var $t = $(this).children(":eq(" indexColumn ")");
for (var d = 0; d < data.length; d) {
if ($t.is(":not(:containsIN('" data[d] "'))")) {
return true;
}
}
return false;
}).hide();
I am getting column index by
$("#" tablename).find('th').each(function () {
if ($(this).find('span').text() == 'tags') {
d = $(this).index();
var e = $("#" tablename).find('tr:eq(' row.index() ')').find('td:eq(' d ')');
}
});
I hope I am making sense on this.
CodePudding user response:
In this example, we just use the :last-child
to find the tag column. I think the crux of your question relates to being able to filter the rows based on matching a string or set of strings.
<button filter="HIGHER EDUCATION,RENOVATION">filter 1</button>
we will use this filter
attribute later to split()
on ,
and then match all the whole filter
and hide those that do not match.
var filter = $(this).attr("filter").split(",");
$("table.tg").find("tbody > tr").filter(function(i, r){
var txt = $(r).find(":last-child").text(); //get the text in the last cell
var match = filter.every(s=>(new RegExp(s)).test(txt)); //does the text match all the filters?
return !match; //if it doesnt match return it to be filtered (hidden)
}).hide();
$("button.filter").on("click", function(e){
//show all hidden rows
$("table.tg").find("tbody > tr").show();
//split the filter attribute into an array by commas
var filter = $(this).attr("filter").split(",");
$("table.tg").find("tbody > tr").filter(function(i, r){
var txt = $(r).find(":last-child").text(); //get the text in the last cell
var match = filter.every(s=>(new RegExp(s.trim())).test(txt)); //does the text match all the filters? trimmed just in-case
return !match; //if it doesnt match return it to be filtered (hidden)
}).hide();
});
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0pky{border-color:inherit;text-align:left;vertical-align:top}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button filter="HIGHER EDUCATION,RENOVATION">filter 1</button>
<button filter="HIGHER EDUCATION,SCHOOLS-COLLEGE">filter 2</button>
<button filter="">reset</button>
<table >
<thead>
<tr>
<th >ID</th>
<th >Name</th>
<th >Tags</th>
</tr>
</thead>
<tbody>
<tr>
<td >1</td>
<td >Test1</td>
<td >AIRPORT TERMINAL,AVIATION,HANGAR,MAINTENANCE BUILDING,NEW CONSTRUCTION,OFFICE</td>
</tr>
<tr>
<td >2</td>
<td >Test2</td>
<td >HIGHER EDUCATION,OFFICE,RENOVATION,SCHOOLS-COLLEGE</td>
</tr>
<tr>
<td >3</td>
<td >Test3</td>
<td >HIGHER EDUCATION,REMODEL,SCHOOLS-COLLEGE</td>
</tr>
<tr>
<td >4</td>
<td >Test4</td>
<td >APARTMENT,HIGHER EDUCATION,NEW CONSTRUCTION,SCHOOLS - COLLEGE</td>
</tr>
</tbody>
</table>
CodePudding user response:
The following example are agnostic plain JavaScript functions. The only things you must know is if there's a <table>
with text in a specific column. In OP:
// USAGE
const dataArray = tableData('table', 2);
// EITHER
filterTerms(dataArray, "HIGHER EDUCATION", "renovation");
// OR
const keywords = ["HIGHER EDUCATION", "RENOVATION"];
filterTerms(dataArray, keywords)
Details are commented in example below
//Utility function
const log = data => console.log(JSON.stringify(data));
/**
* Extract text from a given tbody at a given column.
* @param {string} selector - CSS selector of <table>
* @param {number} column - index number of <td> within each row that has
* the text to extract (@default - 0)
* @returns {array<mixed>} contents of returned array:
* [DOM Object of <tbody>, [[Strings], [Strings],...]]
*/
const tableData = (selector, column = 0) => {
// Reference <tbody>
const T = document.querySelector(selector).tBodies[0];
/*
Collect all <tr> in <tbody> into a HTML Collection then convert it into
an array
*/// Get qty of rows
const rows = [...T.rows];
const rQty = rows.length;
// Define empty array outside of loop
let data = [];
/*
On each <tr> get the text of the <td> located at the index of @column
*/// Convert text into array and add array to >data<
for (let r = 0; r < rQty; r ) {
let tags = rows[r].cells[column].textContent;
tags = tags.split(',');
data.push(tags);
}
//log(data);
// Return mixed array (see @returns)
return [T, data];
};
/**
* Filter a given <table> by completely matching a given string array or
* string(s) vs. the data extracted by tableData()
* All non-matching rows are removed.
* @param {array<mixed>} tData - (see tableData @returns)
* @param {array<string> OR string(s)} terms - Strings to search for
*/
const filterTerms = (tData, ...terms) => {
/*
Check >terms< is an array or strings.
If it's an array, flatten it.
If it's strings put them in an array.
*/
//log(terms);
let keywords = Array.isArray(terms) ? terms.flat() : [...terms];
// Ensure strings are uppercase
const keys = keywords.map(str => str.toUpperCase());
/*
>tData<
[ tbody, [[text of row 0], [text of row 1],...] ]
>tData[0]< = tbody
>tData[1]< = the extracted text of each row at the cell at
>column< index
*/
tData[1].forEach((row, idx) => {
/*
Return true if .every() word of >keys< is present in the current
array in >tData[1]< If it is true...
*/
if (keys.every(term => row.includes(term))) {
//... remove .hide from <tr>..
tData[0].rows[idx].classList.remove('hide');
} else {
// ...otherwise add .hide to <tr>
tData[0].rows[idx].classList.add('hide');
}
});
};
// Save return of tableData() in a variable
const dataArray = tableData('table', 2);
// Each array is for the second @param
const keywords1 = ["HIGHER EDUCATION", "RENOVATION"];
const keywords2 = ["HIGHER EDUCATION", "SCHOOLS-COLLEGE"];
/*
The second @param >terms< can be an array of strings OR
one or more strings (see next line)
*/
filterTerms(dataArray, "HIGHER EDUCATION", "RENOVATION");
//filterTerms(dataArray, keywords1);
//filterTerms(dataArray, keywords2);
table {
border-collapse: collapse;
border-spacing: 0;
border: black solid 1px;
}
td,
th {
border: black solid 1px;
overflow: hidden;
padding: 10px 5px;
text-align: left;
vertical-align: top
}
.hide {
display: none;
}
<table><thead><tr><th>ID<th>Name<th>Tags<tbody><tr><td>1<td>Test1<td>AIRPORT TERMINAL,AVIATION,HANGAR,MAINTENANCE BUILDING,NEW CONSTRUCTION,OFFICE<tr><td>2<td>Test2<td>HIGHER EDUCATION,OFFICE,RENOVATION,SCHOOLS-COLLEGE<tr><td>3<td>Test3<td>HIGHER EDUCATION,REMODEL,SCHOOLS-COLLEGE<tr><td>4<td>Test4<td>APARTMENT,HIGHER EDUCATION,NEW CONSTRUCTION,SCHOOLS-COLLEGE</table>