Convert HTML table to javascript array using class names - javascript

How do you convert an HTML table to a javascript array using the tags's class names as the array values?
Say we have the following HTML code:
<table class="grid">
<tr>
<td class="available"></td>
<td class="busy"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
<tr>
<td class="busy"></td>
<td class="available"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
</table>
I want the array to look like: [["available","busy","busy","available"],["busy","available","busy","available"]]
I have tried the following:
var myTableArray = [];
$("table#grid tr").each(function() {
var arrayOfThisRow = [];
var tableData = $(this).find('td');
if (tableData.length > 0) {
tableData.each(function() { arrayOfThisRow.push($(this).text()); });
myTableArray.push(arrayOfThisRow);
}
});
console.log(myTableArray);
but it is printing an empty array as the td tags contain no text. I then tried replacing
$(this).text()
with
$(this).className()
but that did not work. Any suggestions?

map is the way to go.
jQuery's $.map is a little weird in that it seems to think it's ok to flatten mapped arrays without asking and we're not going to fix it so you have to couch the mapped array in an array.
// Cache the rows
const rows = $('.grid tr');
// `map` over each row...
const arr = $.map(rows, row => {
// Find the row cells...
const cells = $(row).find('td');
// ...and return an array of each cell's text
return [$.map(cells, cell => $(cell).text())];
});
console.log(arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="grid">
<tr>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
<tr>
<td class="busy">busy</td>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
</table>
Alternatively, if you wanted a vanilla JS solution, you can just pick up the rows with querySelectorAll, and then iterate over them with map, then return the text from the cell (assuming that you fix the HTML).
(Note: [...nodelist] is shorthand for creating an array from a nodelist so that map can work. You could also use Array.from(nodelist)).
// Cache the rows
const rows = document.querySelectorAll('.grid tr');
// `map` over each row...
const arr = [...rows].map(row => {
// Find the row cells...
const cells = row.querySelectorAll('td');
// ...and return an array of each cell's text
return [...cells].map(cell => cell.textContent);
});
console.log(arr);
<table class="grid">
<tr>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
<tr>
<td class="busy">busy</td>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
</table>

Vanilla JS Solution
Get and Make array with <tr>
Map every <tr> and make array with <td> elements
Map only class names from every <td>
The example will return ARRAY from CLASS element names.
Example:
var res = [...document.querySelectorAll('.grid tr')] // 1. Get and Make array with <tr>
.map((el) => [...el.children] // 2. Map every <tr> and make array with <td> elements
.map(e => e.getAttribute('class'))); // 3. Map only class names from every <td>
console.log(res);
<table class="grid">
<tr>
<td class="available"></td>
<td class="busy"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
<tr>
<td class="busy"></td>
<td class="available"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
</table>
This example will make ARRAY from the text content of the table.
Example:
var res = [...document.querySelectorAll('.grid tr')] // 1. Get and Make array with <tr>
.map((el) => [...el.children] // 2. Map every <tr> and make array with <td> elements
.map(e => e.innerText)); // 3. Map only text content from every <td>
console.log(res);
<table class="grid">
<tr>
<td>available</td>
<td>busy</td>
<td>busy</td>
<td>available</td>
</tr>
<tr>
<td>busy</td>
<td>available</td>
<td>busy</td>
<td>available</td>
</tr>
</table>

Related

How to get all the elements of a column from html table and validate if a particular string exists in that list

I have a html table with multiple rows and columns. I want to pull all the values by column id and compare with some matching string. If matches i want to enable a button on the page.
Could you please let me know how to refer the column by id in $(document).ready function.
Here is the table
<table id="data" class="table">
<thead>
<tr class="DataT1">
<th>Id</th>
<th>Name</th>
<th>Place</th>
</tr>
</thead>
<th:block th:each="it : ${data}">
<tr>
<td th:text="${it.id}">id</td>
<td th:text="${it.name}">name</td>
<td th:text="${it.place}">place</td>
</tr>
</th:block>
</table>
Button:
style="visibility:hidden">Submit
$(document).ready(function(){
//here i want to pull the place column and verify if one of the
places matches my input string enable submit button
$("#submitbutton").css("visibility", "visible");
}
}
This function will take all information inside you td and search for the string you looking for :
But i cannot get the point that you search for a particular string instead of searching for an object.
const addresses = [...document.querySelectorAll(".address")];
const serchFromList = (arr, str) => {
return arr.map(el =>
el = el.innerHTML
).filter(el => el === str)
}
console.log(serchFromList(addresses, "NY"))
/* in case you want a boolean you can use some*/
const isAddressExist = (arr, str) => {
return arr.map(el =>
el = el.innerHTML
).some(el => el === str)
}
console.log(isAddressExist(addresses, "NY"))
<table id="data" class="table">
<thead>
<tr class="DataT1">
<th>Id</th>
<th>Name</th>
<th>Place</th>
</tr>
</thead>
<th>
<tr>
<td>4545</td>
<td>5454</td>
<td>65687</td>
</tr>
<tr>
<td>aziz</td>
<td>david</td>
<td>paul</td>
</tr>
<tr>
<td class='address'>NY</td>
<td class='address'>MTL</td>
<td class='address'>BC</td>
</tr>
</th>
</table>
Should be pretty doable with XPath if you don't want to add extra attributes to Place cell. Just find out the position of Place column and get the text from the same position of <td>.
// Get the table node first
let node = document.getElementById('data')
// Find out position of `Place` column
let nth = document.evaluate('count(//th[text()="Place"]/preceding-sibling::*)+1', node).numberValue
// Get all the place cell by the position
let placeCells = document.evaluate(`//td[position()=${nth}]`, node)
// Get all the place names
let places = [],
placeNode = placeCells.iterateNext()
while (placeNode) {
places.push(placeNode.textContent)
placeNode = placeCells.iterateNext()
}
console.log(places)
// ['NYC', 'SF', 'LA']
<table id="data" class="table">
<thead>
<tr class="DataT1">
<th>Id</th>
<th>Name</th>
<th>Place</th>
</tr>
</thead>
<tbody>
<tr>
<td>0001</td>
<td>Mary</td>
<td>NYC</td>
</tr>
<tr>
<td>0002</td>
<td>John</td>
<td>SF</td>
</tr>
<tr>
<td>0003</td>
<td>Bob</td>
<td>LA</td>
</tr>
</tbody>
</table>

Highlight multiple rows in several tables

I have a page with several rows containing information, made by several users. I'm looking for a way to highlight the all the users rows on mouseover.
This "Highlight multiple items on hover's condition" almost solved my problem, but since the classes or id's in my problem are dynamic from a database, and would contain an identifier from the DB and are unique each time. I have not been able to apply it.
Example code: https://jsfiddle.net/3cehoh78/
<table class="testtable">
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 1a</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 2a</td>
<td class="cellclass">Frodo</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 3a</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 4a</td>
<td class="cellclass">Legoman</td>
<td class="cellclass">data</td>
</tr>
</table>
<br>
<br>
<table class="testtable">
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 1b</td>
<td class="cellclass">Sauron</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 2b</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 3b</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 4b</td>
<td class="cellclass">Legoman</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 5b</td>
<td class="cellclass">Frodo</td>
<td class="cellclass">data</td>
</tr>
</table>
In this example, I want all the rows with "Sam" to be highlighted on mouseover on one of them, so rows 1a,3a,2b,3b.
I was thinking of adding a class to all the Sam rows when generating the tables (Sam has a unique user ID), but how do I then change css that affects all the rows on mouseover (and not just one).
Please note that I cant pre-add css classes for all the unique userID's, this is just an example.
Here a solution with JQuery https://jsfiddle.net/3cehoh78/5
$(document).ready(function() {
$( "tr" ).hover(function() {
var search = $(this).find("td:eq(1)").text();
$( ".highlight" ).removeClass("highlight");
$("tr:contains('"+search+"')").addClass("highlight");
}); /* END HOVER */
}); // end document ready
Simple solution without using jQuery and co: https://jsfiddle.net/3cehoh78/3/
var rows = [].slice.call(document.querySelectorAll('.testtable tr'));
rows.forEach(function(row) {
row.addEventListener('mouseover', function() {
resetHighlighting();
var name = row.querySelector('td:nth-child(2)').textContent;
rows.forEach(function(r) {
if (r.querySelector('td:nth-child(2)').textContent === name) {
r.classList.add('highlighted');
}
});
});
});
function resetHighlighting() {
rows.forEach(function(row) {
row.classList.remove('highlighted');
});
}
Here's another way using vanilla-JavaScript.
var tds = document.querySelectorAll('td');
var highlight = function () {
// take this person's name from the 2nd cell
var name = this.parentNode.children[1].innerHTML;
// highlight cells with same name
tds.forEach(function (td) {
var tr = td.parentNode;
// compare other's person name with this person name
// highlight if there is a match
tr.classList.toggle('highlight', tr.children[1].innerHTML === name)
});
}
// attach an event listener to all cells
tds.forEach(function (td) {
td.onmouseover = highlight;
});
Demo

How to select the last cell from table which is not null, using JQuery

I have a table with following rows and cells:
<table id='table1'>
<tr id='row1'>
<th>index</th>
<th>Product</th>
<th>Description</th>
</tr>
<tr id='row2' name='row'>
<td name='index'>1</td>
<td name='product'>Apples</td>
<td name='description'>fruits</td>
</tr>
<tr id='row3' name='row'>
<td name='index'>2</td>
<td name='product'>Bananas</td>
<td name='description'>fruits</td>
</tr>
<tr id='row4' name='row'>
<td name='index'>3</td>
<td name='product'>Carrots</td>
<td name='description'>vegetables</td>
</tr>
<tr id='row5' name='row'>
<td name='index'></td>
<td name='product'></td>
<td name='description'></td>
</tr>
</table>
I need to select the value for the last td with name='index' which is not null. Anyone has any idea how can this be done.
Use the following selector :
$('td[name=index]:not(:empty):last')
For purely educational purposes, here is a non jQuery version:
function getLastNonEmptyCell(tableSelector) {
//Find parent table by selector
var table = document.querySelector(tableSelector)
//Return null if we can't find the table
if(!table){
return null;
}
var cells = table.querySelectorAll("td")
var lastNonEmptyCell = null;
//Iterate each cell in the table
//We can just overwrite lastNonEmptyCell since it's a synchronous operation and the return value will be the lowest item in the DOM
cells.forEach(function(cell) {
//!! is used so it's so if it is not null, undefined, "", 0, false
//This could be changed so it's just cell.innerText.trim() !== ""
if (!!cell.innerText) {
lastNonEmptyCell = cell;
}
})
return lastNonEmptyCell;
}
var cell = getLastNonEmptyCell("#table1")
Edit
As #squint suggested this can be done much more succintly:
function lastNonEmptyCell(tableSelector) {
//Since we want the last cell that has content, we get the last-child where it's not empty. This returns the last row.
var row = document.querySelectorAll(tableSelector + " td:not(:empty):last-child")
//Just grabbing the last cell using the index
return row[row.length - 1]
}

Breadth-first traversal in Javascript

I want give every element in a table a generated id. See this html table below:
<table>
<tbody>
<tr>
<td>A1</td>
<td>A2</td>
<td>
A3
</td>
</tr>
<tr>
<td>B1</td>
<td>B2</td>
<td>
B3
</td>
</tr>
<tr>
<td>C1</td>
<td>C2</td>
<td>C3</td>
</tr>
</tbody>
</table>
I want to give each element an id using breadth-first traversal. So, the result becomes like this:
<table>
<tbody id="0">
<tr id="1">
<td id="4">A1</td>
<td id="5">A2</td>
<td id="6">
A3
</td>
</tr>
<tr id="2">
<td id="7">B1</td>
<td id="8">B2</td>
<td id="9">
B3
</td>
</tr>
<tr id="3">
<td id="10">C1</td>
<td id="11">C2</td>
<td id="12">C3</td>
</tr>
</tbody>
</table>
I have tried the each() function in jQuery to generate the id for every element in that table, but the traversal algorithm used in each() function is pre order traversal.
Can anyone suggest me the Javascript code to do this?
var n = 0
var level = $("table");
while (level.children().length) {
level = level.children().each(function(_, el) {
el.id = n++;
})
}
DEMO: http://jsfiddle.net/J5QMK/
If you want to avoid the redundant .children() call, you can do this:
while ((level = level.children()).length) {
level.each(function (_, el) {
el.id = n++;
})
}
DEMO: http://jsfiddle.net/J5QMK/1/
A common way to do a breadth-first search is to use a queue as follows:
jQuery(document).ready(function () {
var ctr = 0;
var queue = [];
queue.push(jQuery("table").children()); // enqueue
while (queue.length > 0) {
var children = queue.shift(); // dequeue
children.each(function (ix, elem) {
queue.push( // enqueue
jQuery(elem).attr("id", ctr++).children();
);
console.log(elem.tagName + ": " + elem.id);
});
}
});

Sum associative array key values and group it by key

I have table as follows :
<table>
<thead>
<th>PRODUCT</th>
<th>QUANTITY</th>
<th>AREA</th>
<th>PRICE</th>
<th>TOTAL</th>
<tr>
<td id="name">SWEETS</td>
<td id="qty">10</td>
<td id="area">250</td>
<td id="price">16.50</td>
<td id="total">160.50</td>
</tr>
<tr>
<td id="name"">DRY FOODS</td>
<td id="qty">5</td>
<td id="area">100</td>
<td id="price">10.25</td>
<td id="total">51.25</td>
</tr>
<tr>
<td id="name">FRESH</td>
<td id="qty">20</td>
<td id="area">250</td>
<td id="price">5</td>
<td id="total">100</td>
</tr>
<tr>
<td id="name">MEAT</td>
<td id="qty">10</td>
<td id="area">250</td>
<td id="price">15</td>
<td id="total">150</td>
</tr>
<tr>
<td id="name">FROZEN</td>
<td id="qty">20</td>
<td id="area">300</td>
<td id="price">10</td>
<td id="total">200</td>
</tr>
</table>
So, I want to make an array like {area:total} then grouping array values based on area and sum area values.
Like :
AREA 250 : 410.5
AREA 100 : 51.25
AREA 300 : 200
I tried as follow which I got it array but I don't know how can I grouping the areas ( I used setInterval function because employees can remove or change the area values)
setInterval(function() {
var $row = $(this).closest("tr");
var sasData = [];
$row.each(function(i) {
var sasValue = parseFloat($row.find("#area").val());
var totValue = parseFloat($row.find("#total").val());
sasData.push({sas:sasValue, tot:totValue});
console.log(sasData);
});
function compressedArray(original) {
var compressed = [];
};
}, 1500)
Could you please show me the way how can we handle this issue?
This JSFiddle should solve your problem. I've also fixed your missing thead, your double quote in the DRY FOODS td, and changes id's to classes:
http://jsfiddle.net/Q9nrf/1/
var areas = {};
$("tr").each(function() {
var area = $(this).find("td.area").text();
if (area != "") {
var total = parseFloat($(this).find("td.total").text());
if (!areas.hasOwnProperty(area)) {
areas[area] = 0;
}
areas[area] += total;
}
});
console.log(areas);
You will need to change the id values to some other attribute, say class.
Loop over the rows (use the tbody element to skip the header) and collect values from the elements with the classes you're after. You will need to use an array to store them, as you can't order the properties of an object and each property must have a unique name.
id should be unique. so change <td id="area">250</td> to <td class="area">250</td>
then just call:
o = {};
$("td.area").each(function(){
key = o[$(this).text()];
if (!key) key = 0;
key += parseFloat( $(this).closest("tr").find(".total").text());
});
then you have on object contains key-value [key=area code, value=total]

Categories