I have a table for example:
head1 head2 head3
x 1 BLA
y 2 BLA
z 3 BLA
I want to be able to do Group by function liek SQL:
select count(head2) , head3
group by head3
And the output will be something like:
3 BLA
iv'e been told that i able to do this only on the server side but I want to do that on the client side.
the table is in format of HTML
<table>
<thead>
<tr>
<th>head1</th>
<th>head2</th>
<th>head3</th>
</tr>
</thead>
<tbody>
<tr>
<td>x</td>
<td>1</td>
<td>BLA</td>
</tr>
<tr>
<td>y</td>
<td>2</td>
<td>BLA</td>
</tr>
<tr>
<td>z</td>
<td>3</td>
<td>BLA</td>
</tr>
</tbody>
</table>
So just for fun...
function countByGroup(data, val)
{
var i, count = 0;
for (i=0; i<data.length; i++)
{
if (val === data[i].head3)
{
count++;
}
}
return count;
}
var payload = [
{
head1: "x",
head2: 1,
head3: "BLA"
},
{
head1: "y",
head2: 2,
head3: "BLA"
},
{
head1: "z",
head2: 3,
head3: "THER"
}
];
window.alert(countByGroup(payload, "BLA") + " BLA");
window.alert(countByGroup(payload, "THER") + " THER");
Fiddleige.
There's no, nice, LINQ-like or SQL-like way of doing it. If you want to be able to change column name, we can do that too, but, again, it depends on what your data looks like on the client.
If you wanted to know more than one val type each time, you'd want to edit the function to check for more than one count at a time, otherwise you're looping needlessly.
And, again, please be careful sorting tens of thousands of rows of data if it's tied to your UI at all.
OK!
I watched #ruffin answer and I just changed it a little for my own case:
<table>
<thead>
<tr>
<th>head1</th>
<th>head2</th>
<th>head3</th>
</tr>
</thead>
<tbody>
<tr class="trtest">
<td>x</td>
<td>1</td>
<td>BLA</td>
</tr>
<tr class="trtest">
<td>y</td>
<td>2</td>
<td>BLA</td>
</tr>
<tr class="trtest">
<td>z</td>
<td>3</td>
<td>THER</td>
</tr>
</tbody>
</table>
<script>
var payload = [];
function countByGroup(data, val)
{
var i, count = 0;
for (i=0; i<data.length; i++)
{
if (val === data[i].head3)
{
count++;
}
}
return count;
}
for(i = 0 ; i < document.getElementsByClassName('trtest').length ; i++)
{
var obj = new Object();
obj.head1 = document.getElementsByClassName('trtest')[i].getElementsByTagName('td')[0].innerHTML;
obj.head2 = document.getElementsByClassName('trtest')[i].getElementsByTagName('td')[1].innerHTML;
obj.head3 = document.getElementsByClassName('trtest')[i].getElementsByTagName('td')[2].innerHTML;
payload.push(obj)
}
window.alert(countByGroup(payload, "BLA") + " BLA");
window.alert(countByGroup(payload, "THER") + " THER");
</script>
this worked for me very good for this current case.
Thank you all!
Related
I have an HTML table and I need to define a function that should grab the data from the table and build an array of objects that contains table data. Outside the function I have to declare a variable and assign the returned value from the function.
Thanks in advance.
HTML
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
JS
function buildTableData() {
let tbody = document.getElementsByTagName("tbody")[0];
let rows = tbody.children;
let people = [];
for (let row of rows) {
let person = {};
let cells = row.children;
person.rating = cells[0].textContent;
person.review = cells[1].textContent;
person.favoriteFood = cells[2].textContent;
people.push(person);
return people;
}
let data = people;
console.log(data);
}
You can get all the elements by using querySelectorAll('td'). Then use map to to get only the text of it and return this.
function buildTableData() {
const elements = [...document.querySelectorAll('td')];
return elements.map(x => {
return {content : x.innerHTML}
});
}
console.log(buildTableData());
<body>
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/7.3.1/acorn.js" integrity="sha512-4GRq4mhgV43mQBgKMBRG9GbneAGisNSqz6DSgiBYsYRTjq2ggGt29Dk5thHHJu38Er7wByX/EZoG+0OcxI5upg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn-walk/7.2.0/walk.js" integrity="sha512-j5XDYQOKluxz1i4c7YMMXvjLLw38YFu12kKGYlr2+w/XZLV5Vg2R/VUbhN//K/V6LPKuoOA4pfcPXB5NgV7Gwg==" crossorigin="anonymous"></script>
<script src="index.js"></script>
</body>
You can try using querySelectorAll() and map() like the following way:
function buildTableData() {
let rows = document.querySelectorAll('tbody tr');
let data = Array.from(rows).map(function(tr){
return {
rating: tr.querySelectorAll('td:nth-child(1)')[0].textContent,
review: tr.querySelectorAll('td:nth-child(2)')[0].textContent,
favoriteFood: tr.querySelectorAll('td:nth-child(3)')[0].textContent
};
});
console.log(data);
}
buildTableData();
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
You want a loop, and each review to be an object that is appended to an array of reviews is what I'm assuming
var reviews = [];
var tbody = document.querySelectorAll("tbody")[0];
var TRs = tbody.querySelectorAll("tr");
for (var a = 0; a < TRs.length; a++) {
var TDs = TRs[a].querySelectorAll("td");
var review = {
name: "",
rating: "",
review: ""
};
//These assume the order of your table columns don't change
review.name = TDs[0].innerHTML;
review.rating = TDs[1].innerHTML;
review.review = TDs[2].innerHTML;
reviews.push(review);
}
Your reviews array should have everything in there just as you wanted. I assumed the third column was "review" instead of "favorite food"
I am learning Javascript, in a school assignment we have to by using a loop count how many games was made in 2014.
It does not return anything in the console, where have I gone wrong?
var allGames = document.getElementsByTagName('td');
var array = Array.prototype.slice.call(allGames, 0)
var games14 = 0;
for (var i = 0; i < array.length; ++i) {
if (array[i] == 2014) {
games14++;
console.log(games14)
}
}
<table id="games">
<thead>
<tr>
<th>Titel</th>
<th>Genre</th>
<th>Årstal</th>
</tr>
</thead>
<tbody id="games_tbody">
<tr class="horror">
<td class="title">Outlast</td>
<td>Horror</td>
<td>2013</td>
</tr>
<tr class="rpg">
<td class="title">Dragon Age: Inquisition</td>
<td>Role-playing Game</td>
<td>2014</td>
</tr>
<tr class="rpg">
<td class="title">Skyrim</td>
<td>Role-playing Game</td>
<td>2011</td>
</tr>
<tr class="horror">
<td class="title">Amnesia: The Dark Descent</td>
<td>Horror</td>
<td>2010</td>
</tr>
<tr class="simulator">
<td class="title">Scania Truck Driving Simulator</td>
<td>Simulator</td>
<td>2012</td>
</tr>
<tr class="horror">
<td class="title">Five Nights at Freddy’s</td>
<td>Horror</td>
<td>2014</td>
</tr>
<tr class="simulator">
<td class="title">Sims 4</td>
<td>Simulator</td>
<td>2014</td>
</tr>
<tr class="rts" id="last">
<td class="title">Warcraft III: Reign of Chaos</td>
<td>Real-time Strategy</td>
<td>2002</td>
</tr>
</tbody>
</table>
You need to check for its text:
var allGames = document.getElementsByTagName('td');
...
if (array[i].innerHTML == '2014') {
OR,
if(array[i].innerText == '2014' || array[i].textContent == '2014'){
No need to do Loop through elements in a HTML table.. You can simply use regular expressions to count all occurrences within the games_tbody:
var games14 = document
.getElementById('games_tbody')
.innerText
.match(/2014/g)
.length;
console.log('Games made in 2014:', games14);
<table id="games">
<thead>
<tr><th>Titel</th><th>Genre</th><th>Årstal</th></tr>
</thead>
<tbody id="games_tbody">
<tr class="horror"><td class="title">Outlast</td><td>Horror</td><td>2013</td></tr>
<tr class="rpg"><td class="title">Dragon Age: Inquisition</td><td>Role-playing Game</td><td>2014</td></tr>
<tr class="rpg"><td class="title">Skyrim</td><td>Role-playing Game</td><td>2011</td></tr>
<tr class="horror"><td class="title">Amnesia: The Dark Descent</td><td>Horror</td><td>2010</td></tr>
<tr class="simulator"><td class="title">Scania Truck Driving Simulator</td><td>Simulator</td><td>2012</td></tr>
<tr class="horror"><td class="title">Five Nights at Freddy’s</td><td>Horror</td><td>2014</td></tr>
<tr class="simulator"><td class="title">Sims 4</td><td>Simulator</td><td>2014</td></tr>
<tr class="rts" id="last"><td class="title">Warcraft III: Reign of Chaos</td><td>Real-time Strategy</td><td>2002</td></tr>
</tbody>
</table>
array[i] == 2014
Everything in the array will be an HTML Table Data Cell Element object.
Nothing in the array will be the Number 2014.
You need to read the text content from the element and then compare that.
I think this is a better way.
var table = document.getElementById("games");
count = 0;
for (let i = 0; row = table.rows[i]; i++) {
if (row.cells[2].innerHTML === "2014"){
count++;
}
/*
for (let j = 0; col = row.cells[j]; j++) {
if (col.innerHTML === "2014"){
count++;
}
}
*/
}
console.log(count);
The commented code is for checking every item on a single row.
i have HTML table, how to do row span on specific columns using j-query. i want to row span on specific columns as can be seen in fiddle demo
here is Demo
after HTML Result should be using jquery
Below is HTML.
<p>Before</p>
<table width="200" border="1">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>4</td>
<td>1</td>
<td>1</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>5</td>
<td>1</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>1</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
</tr>
<tr>
<td>13</td>
<td>131</td>
<td>4155</td>
<td>464</td>
<td>46</td>
</tr>
</table>
<p>After Sholud be using jquery (dynamically)</p>
<table width="200" border="1">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td rowspan="2">1</td>
<td>4</td>
<td rowspan="2">1</td>
<td rowspan="2">1</td> <td>5</td>
</tr>
<tr>
<td>5</td>
<td>3</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>1</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
</tr>
<tr>
<td>13</td>
<td>131</td>
<td>4155</td>
<td>464</td>
<td>46</td>
</tr>
</table>
i try this,but it does not work
groupTable($('#gvSearchRecord tr:has(td)'), 2,2);
$('#gvSearchRecord .deleted').remove();
function groupTable($rows, startIndex, total) {
if (total === 0) {
return;
}
var i, currentIndex = startIndex, count = 1, lst = [];
var tds = $rows.find('td:eq(' + currentIndex + ')');
var ctrl = $(tds[0]);
lst.push($rows[0]);
for (i = 1; i <= tds.length; i++) {
if (ctrl.text() == $(tds[i]).text()) {
count++;
$(tds[i]).addClass('deleted');
lst.push($rows[i]);
}
else {
if (count > 1) {
ctrl.attr('rowspan', count);
groupTable($(lst), startIndex + 1, total - 1)
}
count = 1;
lst = [];
ctrl = $(tds[i]);
lst.push($rows[i]);
}
}
}
I've updated your JSFiddle example:
http://jsfiddle.net/bn9m1p6k/6/
I've removed the list parameter and put the rowspan parameter in a data-attribute.
When the rowspan is directly applied, the table is changed and the following checks aren't correct.
Both the delete and rowspan are applied after all cells have been traversed.
This is the full code:
groupTable($('#gvSearchRecord tr:has(td)'), 0,5);
$('#gvSearchRecord .deleted').remove();
$('#gvSearchRecord td[data-rowspan]').each(function(){
$(this).attr('rowspan', $(this).attr('data-rowspan'));
});
function groupTable($rows, startIndex, total) {
if (total === 0) {
return;
}
var i, currentIndex = startIndex, count = 1, lst = [];
var tds = $rows.find('td:eq(' + currentIndex + ')');
var ctrl = $(tds[0]);
for (i = 1; i < tds.length; i++) {
if (ctrl.text() == $(tds[i]).text()) {
count++;
$(tds[i]).addClass('deleted');
}
else {
if (count > 1) {
ctrl.attr('data-rowspan', count);
}
count = 1;
//lst = [];
ctrl = $(tds[i]);
//lst.push($rows[i]);
}
}
groupTable($rows, startIndex + 1, total - 1);
}
DEMO FIDDLE
The easiest way i found was to create a 2D array and then traverse it in reverse. This was the case, as rowspan works by pushing itself down, and if you remove a matching element from below, you then have trouble matching to the element above it when more than 2 are on top of one another.
There are assumptions that both your code and mine are making:
There is no header table row (can be fixed by changing the initial processArray element assignment selectors
No cells have colspan (if this is not the case, i will happily re-examine my code)
JS
var processArray = [];
// make things easier to traverse in reverse by first creating a 2D array of the table
$('#before tr').each(function(i){
var processRow = [];
$(this).find('td').each(function(){
processRow.push($(this));
});
processArray.push(processRow);
});
function computeDuplicates(){
// we are starting at the last row and working up to row 2
// we dont need to look at the first row, as we're looking at it from the row below
for(var x = processArray.length - 1; x > 0 ; x--){
for(var y = 0; y < processArray[x].length; y++){
if(processArray[x][y].text() == processArray[x-1][y].text()){ //if the cell above has the same text as the current cell
var currentRowSpan = processArray[x][y].attr('rowspan'); // get the current cell rowspan
if(typeof currentRowSpan == "undefined"){ // no rowspan found on current cell
currentRowSpan = 2; // set default of 2
}else{
currentRowSpan = parseInt(currentRowSpan, 10) + 1; // increase by 1
}
// apply currentRowSpan to above cell and delete current cell
processArray[x-1][y].attr('rowspan', currentRowSpan);
processArray[x][y].remove();
}
}
}
processArray = []; // remove from memory
}
computeDuplicates();
http://jsfiddle.net/9sKwJ/66/
tr.spacer { height: 40px; }
$.tablesorter.addWidget({
id: 'spacer',
format: function(table) {
var c = table.config,
$t = $(table),
$r = $t.find('tbody').find('tr'),
i, l, last, col, rows, spacers = [];
if (c.sortList && c.sortList[0]) {
$t.find('tr.spacer').removeClass('spacer');
col = c.sortList[0][0]; // first sorted column
rows = table.config.cache.normalized;
last = rows[0][col]; // text from first row
l = rows.length;
for (i=0; i < l; i++) {
// if text from row doesn't match last row,
// save it to add a spacer
if (rows[i][col] !== last) {
spacers.push(i-1);
last = rows[i][col];
}
}
// add spacer class to the appropriate rows
for (i=0; i<spacers.length; i++){
$r.eq(spacers[i]).addClass('spacer');
}
}
}
});
$('table').tablesorter({
widgets : ['spacer']
});
<table id="test">
<thead>
<tr>
<th>Name</th>
<th>Number</th>
<th>Another Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test4</td>
<td>4</td>
<td>Hello4</td>
</tr>
<tr>
<td colspan="3">Test4</td>
</tr>
<tr>
<td>Test3</td>
<td>3</td>
<td>Hello3</td>
</tr>
<tr>
<td colspan="3">Test3</td>
</tr>
<tr>
<td>Test2</td>
<td>2</td>
<td>Hello2</td>
</tr>
<tr>
<td colspan="3">Test2</td>
</tr>
<tr>
<td>Test1</td>
<td>1</td>
<td>Hello1</td>
</tr>
<tr>
<td colspan="3">Test1</td>
</tr>
</tbody>
</table>
This sorts just the way I want it if you sort it by the first column, but the other two columns don't maintain the same paired 'tr' sort im looking for.
Any help on this?
Use the expand-child class name on each duplicated row:
<tr>
<td>Test3</td>
<td>3</td>
<td>Hello3</td>
</tr>
<tr class="expand-child">
<td colspan="3">Test3</td>
</tr>
It's defined by the cssChildRow option:
$('table').tablesorter({
cssChildRow: "expand-child"
});
Here is a demo of it in action.
I am new to HTML and JavaScript. I got a problem like this in HTML (This code below only visualize the problem for you to easy to reference.)
<tr>
<td>1</td>
<td>Harry</td>
</tr>
<tr>
<td>2</td>
<td>Simon</td>
</tr>
<td>3</td>
<td>Maria</td>
</tr>
</tr>
<td>4</td>
<td>Victory</td>
</tr>
This is a name list, however the problem is that sometime i need to add more name into this table and I HAVE TO ADD in front of Number 1, so meaning i have to re-write the number list, (EX: 1 1 2 3 4 --> 1 2 3 4 5). I feel that is not a good way.
NOTE: I don't want to change the list number decrease from top to bottom. And this is a HTML file so can't apply PHP
Anyone can help me to make the number to a variable like "i" and a function can help me to fill variable i increment from top to bottom automatically like
<tr>
<td>i</td>
<td>Harry</td>
</tr>
<tr>
<td>i</td>
<td>Simon</td>
</tr>
<td>i</td>
<td>Maria</td>
</tr>
</tr>
<td>i</td>
<td>Victory</td>
</tr>
Function Fill_i for example:
I think that JavaScript should be used in this case. Thanks for your help and suggestion on this problem.
Again: I am not allowed to use PHP or ASP and when I add a new name, I add it manually by HTML.
You can use a css counter - MDN
table {
counter-reset: section;
}
.count:before {
counter-increment: section;
content: counter(section);
}
<table>
<tr>
<td class="count"></td>
<td>Harry</td>
</tr>
<tr>
<td class="count"></td>
<td>Simon</td>
</tr>
<tr>
<td class="count"></td>
<td>Maria</td>
</tr>
<tr>
<td class="count"></td>
<td>Victory</td>
</tr>
</table>
FIDDLE
This should work for you:
<table>
<tr>
<td>Harry</td>
</tr>
<tr>
<td>Simon</td>
</tr>
<tr>
<td>Maria</td>
</tr>
<tr>
<td>Victory</td>
</tr>
</table>
<script>
var tables = document.getElementsByTagName('table');
var table = tables[tables.length - 1];
var rows = table.rows;
for(var i = 0, td; i < rows.length; i++){
td = document.createElement('td');
td.appendChild(document.createTextNode(i + 1));
rows[i].insertBefore(td, rows[i].firstChild);
}
</script>
The script should be placed immediately after your table. It goes through each row of your table and adds an extra cell to the beginning with the incrementing number inside that cell.
JSFiddle Demo
Edit: seems like the other solution posted would work do (was added while I typed this up).
You really should be using PHP to do something dynamic like this, which would become trivial with a single for loop.
However, if you insist on using HTML/Javascript (or perhaps this is a 'static page'...) then what you are asking should be possible.
You could add a class to each of the <td> elements you want to use, so:
<tr>
<td class='personid'>i</td>
<td>Harry</td>
</tr>
<tr>
<td class='personid'>i</td>
<td>Simon</td>
</tr>
<td class='personid'>i</td>
<td>Maria</td>
</tr>
</tr>
<td class='personid'>i</td>
<td>Victory</td>
</tr>
Then you would have a javascript function that does something like this:
var list = document.getElementsByClassName("personid");
for (var i = 1; i <= list.length; i++) {
list[i].innerHTML = i;
}
Are you sure you don't want an ordered list?
<ol>
<li>Fred</li>
<li>Barry</li>
</ol>
<script>
function addRow(index, name){
var tbody = document.getElementById("nameList");
var row = document.createElement("tr");
var data1 = document.createElement("td");
data1.appendChild(document.createTextNode(index));
var data2 = document.createElement("td");
data2.appendChild(document.createTextNode(name));
row.appendChild(data1);
row.appendChild(data2);
tbody.appendChild(row);
}
var name=new Array();
name[0]="Harry";
name[1]="Simon";
name[2]="Maria";
name[3]="Victory";
for(var i=0; i < name.length; i++) {
addRow(i,name[i]);
}
</script>
<html>
<body>
<table id="nameList">
</table>
</body>
</html>
I would say do this (im going to assume you are not going to load in jquery or anything fancy):
<html>
<head>
<script type="text/javascript>
function writeTable(){
// list of names
var myList = [ "name1", "name2", "etc", "etc"];
// your variable to write your output
var outputTable = "<table>";
//the div to write the output to
var outputDiv = document.getElementById("output");
//the loop that writes the table
for (var i=0; i<myList.length; i++){
outputTable += "</tr><td>"+i+"</td><td>"+myList[i]+"</td></tr>";
}
//close the table
outputTable += "</table>";
//write the table
outputDiv.innerHTML = outputTable;
}
</script>
</head>
<body onload=writeTable()>
<div id='output'></div>
</body>
</html>
hope this helps :)
Try this
$(document).ready(function() {
var addSerialNumber = function () {
$('table tr').each(function(index) {
$(this).find('td:nth-child(1)').html(index);
});
};
addSerialNumber();
});