jQuery Filter row by condition - javascript

I have a page containing multiple tabs per region.
Each row in the table has a class with each region that its impacted by.
<tr class="apac emea americas">...</tr>
<tr class="apac emea">...</tr>
When a tab is clicked, it filters out the table and removes anything where the condition is not met.
$('#' + tab).find("#trainingEvents .results tr:not(.Americas.EMEA.APAC)").remove(); <- This is the ALL tab
Each of the tabs is pretty easy to understand except for "Multiple" which is what my question relates to.
The condition needs to be, remove rows that do not contain 2 of the 3 possible regions.
For example:
<tr class="amea apac"></tr> = True
<tr class="apac">...</tr> = False, Remove it
How can I accomplish this filter? Just needs to meet any 2 combinations of the 3 possible options

I'd suggest the following:
// collating the 'regions':
var regions = ['americas', 'emea', 'apac'],
// initialising an array to use, later:
foundClasses = [];
// iterating over the 'tr' elements, filtering them:
$('tr').filter(function () {
// using Array.prototype.forEach to filter the classList of the element:
foundClasses = Array.prototype.filter.call(this.classList, function (c) {
// 'c' is the current class in the classList we're iterating over,
// if it's in the array we return that array to the 'foundClasses':
if (regions.indexOf(c) > -1) {
return c;
}
});
// we keep the the element in the jQuery collection (of 'tr' elements),
// if we have only 1 (or less...) classes found:
return foundClasses.length < 2;
// removing those 'tr' elements:
}).remove();
var regions = ['americas', 'emea', 'apac'],
foundClasses = [];
$('tr').filter(function () {
foundClasses = Array.prototype.filter.call(this.classList, function (c) {
if (regions.indexOf(c) > -1) {
return c;
}
});
return foundClasses.length < 2;
}).remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr class="americas emea">
<td>americas emea</td>
</tr>
<tr class="apac">
<td>apac</td>
</tr>
<tr class="emea">
<td>emea</td>
</tr>
<tr class="americas">
<td>americas</td>
</tr>
<tr class="apac emea">
<td>apac emea</td>
</tr>
</tbody>
</table>
To account for those browsers without access to Array.prototype.filter(), and possibly element.classList:
var regions = ['americas', 'emea', 'apac'],
classes,
foundClasses = [];
$('tr').filter(function() {
// creating an array by splitting the className property by white-space:
classes = this.className.split(/\s+/);
// crudely emptying the initialised array:
foundClasses = [];
// iterating over the array of classes using a for-loop:
for (var i = 0, len = classes.length; i < len; i++) {
// if the current element in the classes array is in the
// foundClasses array:
if (regions.indexOf(classes[i]) > -1) {
// we push the current class into the foundClasses array:
foundClasses.push(classes[i]);
}
}
// as above:
return foundClasses.length < 2;
}).remove();
var regions = ['americas', 'emea', 'apac'],
classes,
foundClasses = [];
$('tr').filter(function() {
classes = this.className.split(/\s+/);
foundClasses = []; // crudely emptying the array
for (var i = 0, len = classes.length; i < len; i++) {
if (regions.indexOf(classes[i]) > -1) {
foundClasses.push(classes[i]);
}
}
return foundClasses.length < 2;
}).remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr class="americas emea">
<td>americas emea</td>
</tr>
<tr class="apac">
<td>apac</td>
</tr>
<tr class="emea">
<td>emea</td>
</tr>
<tr class="americas">
<td>americas</td>
</tr>
<tr class="apac emea">
<td>apac emea</td>
</tr>
</tbody>
</table>
References:
JavaScript:
Array.prototype.indexOf().
Array.prototype.filter().
Array.prototype.push().
element.classList.
jQuery:
filter().
remove().

You use for that the function "filter": (UPDATE after another requirement to filter it)
$("tr").
filter(function(index){
var classes = $(this).attr("class").split(" ");
var regions = "americas,emea,apac,";
var counter = 0;
for(var i = 0, j = classes.length;i < j;i++){
if(regions.indexOf(classes[i] + ",") >= 0){counter++;}
}
return counter != 2;
})
.remove();
That code will remove all the rows with less or more than 2 region classes.
FIDDLE HAS BEEN UPDATED TOO: http://jsfiddle.net/fac5tapz/8/
If you were using a custom attribute, it would be possible to spare some code:
<table border="1">
<tr regions="apac emea"><td>first row do not remove</td></tr>
<tr regions="apac emea"><td>second row do not remove</td></tr>
<tr regions="apac"><td>third will be removed</td></tr>
<tr regions="apac emea americas"><td>fourth will be remove</td></tr>
<tr regions="apac emea"><td>fifth row do not remove</td></tr>
<tr regions="apac emea americas"><td>sixth will be removed</td></tr>
<tr regions="apac"><td>seventh will be removed</td></tr>
<tr regions="americas emea"><td>eighth row do not remove</td></tr>
</table>
$("tr").
filter(function(index){
var regions = $(this).attr("regions").split(" ");
return regions.length != 2;
})
.remove();
Another fiddle for this version: http://jsfiddle.net/dkseknyw/2/

Related

JavaScript - Get Table Column from Index

I need a way to grab a column based on its index. The index number is from looking up a <td> and returning its cellIndex. The reason I want to do this is so I can grab the <col>'s class and apply it to all its <td>s. Each <td> needs the class applied since I'm using a library (List.js) and I won't be able to do any of the sorting/filtering/searching if the individual <td>s are not appropriately labeled (unfortunately, it won't work if only the <col>s have them). Also, the <col>s do not have any other attributes added like ids, names, etc.
function addClassesToCells(table) {
var col = ; // hoping to identify col using index here
var classname = col.className;
// rest of function
}
function getIndex() { // test function to find index
document.addEventListener("click", function (e) {
e = e || window.event;
var target = e.target || e.srcElement;
var td = "TD";
if (target.tagName == td) {
console.log(target.cellIndex);
}
}, false);
}
The goal is getting all <td>s under a particular to apply classes, so if there are other/better ways to do this, then I welcome any. Thank you.
To get the list of cols and tds, use queries and match the indexes.
var table = document.querySelector('table');
var cols = table.querySelectorAll('col');
var trs = table.querySelectorAll('tr');
var tds = [].map.call(trs, tr => tr.querySelectorAll('td'));
for (var i = 0; i < trs.length; i++) {
for (var j = 0; j < tds[i].length; j++) {
tds[i][j].className = cols[j].className;
}
}
.a {
color: red;
}
.b {
color: blue;
}
.c {
color: green;
}
<table>
<colgroup>
<col class="a" />
<col class="b" />
<col class="c" />
</colgroup>
<tr>
<td>Lime</td>
<td>Lemon</td>
<td>Orange</td>
</tr>
<tr>
<td>Green</td>
<td>Yellow</td>
<td>Orange</td>
</tr>
</table>

Why can I filter by int but not string?

Right now I have code that can filter by int:
<input name='tablefilter' type='checkbox' value='1' id='tablefilter1' checked/>
<label for='tablefilter1'>1</label>
<input name='tablefilter' type='checkbox' value='2' id='tablefilter2' checked/>
<label for='tablefilter2'>2</label>
<input name='tablefilter' type='checkbox' value='3' id='tablefilter3' checked/>
<label for='tablefilter3'>3</label>
<table>
<thead>
<tr>
<th>Col1</th>
<th>Col2</th>
<th>Col3</th>
</tr>
</thead>
<tbody id='tablebody'>
<tr>
<td>1</td>
<td>One</td>
<td>First</td>
</tr>
<tr>
<td>2</td>
<td>Two</td>
<td>Second</td>
</tr>
<tr>
<td>3</td>
<td>Three</td>
<td>Third</td>
</tr>
</tbody>
</table>
js
/* Demo filtering table using checkboxes. Filters against first td value */
/* Set 'ready' handler' */
document.addEventListener('DOMContentLoaded', initFunc);
/* When document ready, set click handlers for the filter boxes */
function initFunc(event) {
var filters = document.getElementsByName('tablefilter');
for (var i = 0; i < filters.length; i++) {
filters[i].addEventListener('click', buildAndExecFilter);
}
}
/*
This function gets called when clicking on table filter checkboxes.
It builds a list of selected values and then filters the table based on that
*/
function buildAndExecFilter() {
var show = [];
var filters = document.getElementsByName('tablefilter');
for (var i = 0; i < filters.length; i++) {
if (filters[i].checked) {
show.push(filters[i].value);
}
}
execFilter(show); // Filter based on selected values
}
function execFilter(show) {
/* For all rows of table, see if td 0 contains a selected value to filter */
var rows = document.getElementById('tablebody').getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {
var display = ""; // Default to display
// If it is not found in the selected filter values, don't show it
if (show.indexOf(rows[i].children[0].textContent) === -1) {
display = "none";
}
// Update the display accordingly
rows[i].style.display = display;
}
}
http://jsfiddle.net/2Lm7pytt/3/
However that filter can't filter by a string. If I for example want to use "one" instead of 1, it wouldn't work.
Does anyone know why and what the solution would be?
Thank you
These lines of your execFilter() method,
if (show.indexOf(rows[i].children[0].textContent) === -1) {
display = "none";
}
is only comparing the index 0 which is the numeric value not other columns.
Unless you compare the values with all the columns (all the indexes of rows[i].children) it won't give you the result you want.
So, you might wan't to run a for loop to iterate through all the children of rows[i].children and compare their text.
var foundResult = false;
for ( var counter = 0; counter < rows[i].children.length; counter++ )
{
if (show.indexOf(rows[i].children[0].textContent) != -1)
{
foundResult= true;
break;
}
}
if ( !foundResult )
{
display = 'none';
}
You mean something like this
var flt = ["zero","one","two","three"];
...
var showIt = show.indexOf(rows[i].children[0].textContent) !=-1;
for (var j=0;j<show.length;j++) {
if (flt[show[j]] == rows[i].children[1].textContent) {
showIt=true;
break;
}
}
rows[i].style.display = showIt?"":"none";

Compare participants in one column of the table and make sum from other column, js

I have a table. I'd like to compare participants. If participant have several result points in the table, the script has to return sum of all participant's results. And so on for every participant.
The table is generated from database (".$row["pnt"]."".$row["station"]."".$row["res"]."):
Participant Station Points
aa Some1 1
dd Some1 2
aa sm2 3
dd sm2 4
bb sm3 5
ee sm3 6
For example I've to recieve such a new table:
aa - 4,
dd - 6,
bb - 5,
ee - 6
I've tried to do so:
$(document).ready(function () {
$("body").click(function () {
var rows = $("tbody tr");
var jo = [];
for (var i = 0; i < rows.length; i++) {
for (var j = 1; j <= rows.length; j++) {
var pnt1 = $(rows[i]).find(".pnt").html();
var stations1 = $(rows[i]).find(".station").html();
var pntR1 = $(rows[i]).find(".res").html();
if (pnt1 == $(rows[j]).find(".pnt").html()) {
pntR1 = parseInt(pntR1);
pntR2 = parseInt($(rows[j]).find(".res").html());
jo.push(pnt1, pntR1, pntR2);
break;
}
}
}
console.log(jo);
});
});
But I understood that I'm on a wrong way. Please, help me. I really appreicate if some one could help me on this issue.
Updated after comments:
<table id="pntsRes">
<thead>
<tr>
<th>Участники</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr><td class="pnt">aa</td><td class="station">AES</td><td class="res">1</td></tr><tr><td class="pnt">dd</td><td class="station">AES</td><td class="res">2</td></tr>
<tr><td class="pnt">aa</td><td class="station">Science</td><td class="res">3</td></tr>
<tr><td class="pnt">dd</td><td class="station">Science</td><td class="res">4</td></tr><tr><td class="pnt">bb</td><td class="station">Аэродром</td><td class="res">5</td></tr>
<tr><td class="pnt">ee</td><td class="station">aeroport</td><td class="res">6</td></tr></tbody>
</table>
First, I would consider breaking your solution into three functions - one to extract the data from the HTML (which is a questionable practice in itself), one to transform the data, and one to output the new table. This way, your code is much more maintainable.
function getData() {
var rows = $("tbody tr");
var data = [];
rows.each(function(idx, row){
var pnt = row.find('.pnt').html(),
station = row.find('.station').html()),
res = parseInt(row.find('.res').html());
data.push(pnt, station, res);
});
}
Then I would consider something like this for the second method
// Pass the output from getData() into processData()
function processData(data){
var groupedKeys = {};
var groupedData = data.map(function(datum){
var name = datum[0];
var value = datum[2];
groupedKeys[name] = (groupedKeys[name] || 0) + (value || 0);
});
var transformedData = [];
Object.keys(groupedKeys).forEach(function(key){
transformedData.push([key, groupedKeys[key]]);
});
return transformedData;
}
The last method of course would need to be implemented by yourself, there's a ton that could be improved here, but it could be a good start.
I used an associative array (which is just an object in JavaScript) shown below:
http://jsfiddle.net/a5k6w300/
Changes I made:
var jo = [];
changed to an object instead of an array
var jo = {};
I also added the if(isNaN(object[key]) inside the inner loop in order to make sure that these didn't show as NaN as I continued adding them together.
$(document).ready(function () {
$("body").click(function () {
var rows = $("tbody tr");
var jo = {};
console.log(rows);
for (var i = 0; i < rows.length; i++) {
for (var j = 1; j <= rows.length; j++) {
var pnt1 = $(rows[i]).find(".pnt").html();
var stations1 = $(rows[i]).find(".station").html();
var pntR1 = $(rows[i]).find(".res").html();
if (pnt1 == $(rows[j]).find(".pnt").html()) {
pntR1 = parseInt(pntR1);
pntR2 = parseInt($(rows[j]).find(".res").html());
if(isNaN(jo[pnt1])){
jo[pnt1] = 0;
}
jo[pnt1] += pntR1;
break;
}
}
}
console.log(jo);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="pntsRes">
<thead>
<tr>
<th>Участники</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr>
<td class="pnt">aa</td>
<td class="station">AES</td>
<td class="res">1</td>
</tr>
<tr>
<td class="pnt">dd</td>
<td class="station">AES</td>
<td class="res">2</td>
</tr>
<tr>
<td class="pnt">aa</td>
<td class="station">Science</td>
<td class="res">3</td>
</tr>
<tr>
<td class="pnt">dd</td>
<td class="station">Science</td>
<td class="res">4</td>
</tr>
<tr>
<td class="pnt">bb</td>
<td class="station">Аэродром</td>
<td class="res">5</td>
</tr>
<tr>
<td class="pnt">ee</td>
<td class="station">aeroport</td>
<td class="res">6</td>
</tr>
</tbody>
</table>

3 dimensional Array Javascript

I have a string that I am splitting using string.split(' '); in order to turn the string into an array.
suppose I have these two tables, table1 and table2.
<table border="1" id="table1">
<tr>
<th colspan="2">Image One</th>
</tr>
<tr>
<td style="width:40%;"><img src="airplane.jpg" alt="Image 1"></td>
<td>
<dl>
<dt>airplane</dt>
<dt>flight</dt>
<dt>travel</dt>
<dt>military</dt>
<dt>word war</dt>
<dt>GI</dt>
</dl>
</td>
</tr>
</table>
<table border="1" id="table2">
<tr>
<th colspan="2">Image Two</th>
</tr>
<tr>
<td style="width:40%;"><img src="apple.jpg" alt="Image 1"></td>
<td>
<dl id="tags">
<dt>red</dt>
<dt>apple</dt>
<dt>round</dt>
<dt>fruit</dt>
<dt>healthy</dt>
<dt>doctor</dt>
</dl>
</td>
</tr>
</table>
right now for testing purposes I have an id of tags on table2's dl.
I am using a function to turn that DL (#tags) into an array
function getArray(id) {
var node, list, arrValue;
array = [];
for (node = document.getElementById(id).firstChild;
node;
node = node.nextSibling) {
if (node.nodeType == 1 && node.tagName == 'DT') {
array.push(node.innerHTML);
}
}
console.log(array)
}
in order to check it against my original string to see if any of the values match.
However, I am going to have multiple DT's that the string is going to be check against. Would it be correct to add all the tables into a 3d array and then check the values in the string against the 3d array? or is there a better approach?
UPDATE
The problem is:
I am eventually going to have tables filled with an image and tags. Essentially I want to be able to search those tags against my string (which will be separated into an array) then return the image with the most tags in the string. I am trying to figure out the best way to do that.
Thank you
Rather than an array I would use an Object to store the list of tags, where the keys are the tags and the values are irrelevant.
This would give you O(1) lookup to check whether some other string exists in that list, as opposed to an O(n) lookup if you were using array.indexOf().
The function below will find every DT on the page and then return an object containing a map from each DT's text to the ID of its parent DL.
function makeMap() {
var map = {};
var dls = document.getElementsByTagName('DL');
for (var i = 0, n = dls.length; i < n; ++i) {
var dl = dls[i];
var id = dl.id;
var node = dl.firstChild;
while (node) {
if (node.nodeType == 1 && node.tagName == 'DT') {
var tag = node.textContent || node.innerText; // latter for MSIE
map[tag] = id;
}
node = node.nextSibling;
}
}
return map;
}
Alternatively, in jQuery (with some pure JS mixed in for efficiency):
function makeMap2() {
var map = {};
var $dt = $('dl > dt');
$dt.each(function() {
var tag = this.textContent || this.innerText;
map[tag] = this.parentNode.id;
});
return map;
}
You wouldn't use a three-dimensional array, but only a two-dimensional one with tables and their tags. Or, as Alnitak already mentioned, even better a lookup object:
var map = {};
var dls = document.getElementsByTagName('dl');
for (var i = 0, i < dls.length; i++) {
var tableid = dls[i].id; // identifier?
var dts = dls[i].getElementsByTagName('dt'); // assuming you don't nest them
for (var j = 0; j < dts.length; j++) {
var text = dts[j].textContent || dts[i].innerText;
var tags = text.split(/\s+/);
for (var k=0; k<tags.length; k++)
if (tags[k] in map)
map[tags[k]].push(tableid);
else
map[tags[k]] = [tableid]; // an array
}
}
/* now, map could look like this:
{
word: ["table1"],
war: ["table1"],
red: ["table2"],
double: ["table1", "table2"], // tags in more than one table
…
}
*/
To get the table with the most tags in the string you now can use a function like this, which returns the respective tableids sorted by tag occurence:
function getHighestTables(string) {
var tags = string.split(/\s+/);
var tablecounts = {};
for (var i=0; i<tags.length; i++) {
var tables = map[tags[i]] || [];
for (var j=0; j<tables.length; j++) {
var tableid = tables[j];
if (tableid in tablecounts)
tablecounts[tableid]++;
else
tablecounts[tableid] = 1;
}
}
/* tablecounts might now look like this:
{
table1: 2
table2: 5
}
*/
return Object.keys(tablecounts).sort(function (a, b) {
return tablecounts[b] - tablecounts[a];
});
}

How to check current tag element in javascript?

I've a GridView with three rows like this
<tr>
<th>SlNo</th>
</tr>
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
I've the following code to traverse through the rows
var GridViewRow=GridView.getElementsByTagName('tr')
Here the row length is 3.
I travese through the GridViewRow using for loop .Here how will i get the tag name of the current element ie (th or td).
If the tagname is "TH" it should return and if it is "TD" it should take the value of TD.
How about this
var table = document.getElementById("mytab1");
for (var i = 0, cell; cell = table.cells[i]; i++) {
//iterate through cells
//cells would be accessed using the "cell" variable assigned in the for loop
}
you can also try out
var tbl = document.getElementById('yourTableId');
var rows = tbl.getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++)
{
if(rows[i].getElementsByTagName('td').length > 0)
{
//code to execute
}
else
{
continue;
}
}
var GridViewRow = GridView.getElementsByTagName('tr');
$(GridViewRow).each(function() {
var $this = $(this), td = $this.find('td');
if (td.length === 1) {
console.log(td.text());
}
});
this works for <tr> in which you have exactly one <td> if you use jquery, otherwise in plain javascript try this:
var GridViewRow = GridView.getElementsByTagName('tr'),
len = GridViewRow.length,
td;
while (--len) {
td = GridViewRow[len].getElementsByTagName('td');
if (td.length === 1) {
console.log(td[0].innerHTML);
}
}
});
You can check the tag name with jQuery :
$(this).attr("tag");
Later edit:
For raw javascript, use tagName:
element.tagName

Categories