Home > Software design >  Trying to Filter Html table using array to check string
Trying to Filter Html table using array to check string

Time:03-22

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>

  • Related