I have a 2-column table and I would like to convert the cells into an array using jQuery. I currently have that working, but I would like the array to be "2-column" as well, not sure if that's the right terminology. But basically I want the 2 cells from each row to be part of the same "row" in the array. Currently I have this:
$(function() {
var arr = [];
$('tbody tr').each(function() {
var $this = $(this),
cell = $this.find('td');
if (cell.length > 0) {
cell.each(function() {
arr.push($(this).text());
});
}
});
console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<td>Table heading</td>
</tr>
</thead>
<tbody>
<tr>
<td>Apples</td>
<td>Red</td>
</tr>
<tr>
<td>Bananas</td>
<td>Yellow</td>
</tr>
<tr>
<td>Oranges</td>
<td>Orange</td>
</tr>
<tr>
<td>Cucumbers</td>
<td>Green</td>
</tr>
</table>
How do I make it so that 0 would be Apples, Red and so on?
You can do something like this
$(function() {
var arr = $('tbody tr').get()//convert jquery object to array
.map(function(row) {
return $(row).find('td').get()
.map(function(cell) {
return cell.innerHTML;
}).join(',');
});
console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<td>Table heading</td>
</tr>
</thead>
<tbody>
<tr>
<td>Apples</td>
<td>Red</td>
</tr>
<tr>
<td>Bananas</td>
<td>Yellow</td>
</tr>
<tr>
<td>Oranges</td>
<td>Orange</td>
</tr>
<tr>
<td>Cucumbers</td>
<td>Green</td>
</tr>
</table>
ok you can also do this.
$(function() {
var arr = [];
flag = 0;
$('tbody tr td').each(function() {
if(flag == 0){
arr1 = [];
arr1.push($(this).text());
arr.push(arr1);
flag = 1;
}else{
let arr1 = arr[arr.length-1];
arr1.push($(this).text());
arr[arr.length-1] = arr1;
flag = 0;
}
});
console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<td>Table heading</td>
</tr>
</thead>
<tbody>
<tr>
<td>Apples</td>
<td>Red</td>
</tr>
<tr>
<td>Bananas</td>
<td>Yellow</td>
</tr>
<tr>
<td>Oranges</td>
<td>Orange</td>
</tr>
<tr>
<td>Cucumbers</td>
<td>Green</td>
</tr>
</table>
I'd suggest:
// using Array.from() to convert the Array-like NodeList returned
// from document.querySelectorAll() into an Array, in order to use
// Array.prototype.map():
let array = Array.from(document.querySelectorAll('tbody tr')).map(
// tr: a reference to the current array-element of the Array over
// which we're iterating; using Arrow function syntax:
(tr) => {
// here we return the result of the following expression;
// again using Array.from() to convert the NodeList of
// the <tr> element's children into an Array, again in order
// to utilise Array.prototype.map():
return Array.from(tr.children).map(
// cell is a reference to the current Node of the Array
// of Nodes over which we're iterating; here we implicitly
// return the textContent of each <td> ('cell') Node; after
// using String.prototype.trim() to remove leading/trailing
// white-space:
(cell) => cell.textContent.trim()
);
});
let array = Array.from(document.querySelectorAll('tbody tr')).map(
(tr) => {
return Array.from(tr.children).map(
(cell) => cell.textContent.trim()
);
});
console.log(array);
<table>
<thead>
<tr>
<td>Table heading</td>
</tr>
</thead>
<tbody>
<tr>
<td>Apples</td>
<td>Red</td>
</tr>
<tr>
<td>Bananas</td>
<td>Yellow</td>
</tr>
<tr>
<td>Oranges</td>
<td>Orange</td>
</tr>
<tr>
<td>Cucumbers</td>
<td>Green</td>
</tr>
</table>
References:
Array.from().
Array.prototype.map().
Arrow functions.
document.querySelectorAll().
ParentNode.children.
String.prototype.trim().
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"
It's well known, that 'array of objects' format of data storage is well suitable for data persisting. I'd be extremely grateful if a JavaScript guru helps me with finding the answer to how do I read this HTML-table with vanilla JavaScript and transport data from it into the following array of objects.
I have produced tons of code, mostly comparing two arrays of objects. Unfortunately, I didn't come even closer to a solution.
The table to scrape data from:
<table>
<tbody>
<tr>
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr>
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
Expected outcome to be like so:
[
{"country":"Canada","city":"Toronto"},
{"country":"Canada","city":"Montreal"},
{"country":"Canada","city":"Vancouver"},
{"country":"USA","city":"New York"},
{"country":"USA","city":"Chicago"},
{"country":"USA","city":"Boston"},
{"country":"USA","city":"Washington"},
{"country":"USA","city":"Detroit"},
{"country":"USA","city":"Los Angeles"}
]
The code is valid, unlike the approach:
let theResult = [];
arrayOfCountriesAndCitiesObjects.forEach((item, iIndex) => {
arrayOfCitiesObjects.forEach((elem, eIndex) => {
if(item.city !== elem.city && item.iIndex < elem.eIndex) theResult.push(copy(elem, item));
});
});
function copy(firstObj) {
for (let i = 1; i < arguments.length; i++) {
let arg = arguments[i];
for (let key in arg) {
firstObj[key] = arg[key];
}
}
return firstObj;
}
You could store the value of colSpan === 3 as country and push all other values as city to the result set.
This works with plain Javascript without any libraries.
var result = [],
country = '';
document
.querySelectorAll('table td')
.forEach(td => {
if (td.colSpan === 3) {
country = td.innerHTML;
return;
}
result.push({ country, city: td.innerHTML.trim() });
});
console.log(result);
<table>
<tbody>
<tr>
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr>
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
You can use for to loop thru each tr. Find the td on each tr, If there is only 1, store the text on currentCountry variable. If more than one, push the object to the result variable.
var currentCountry = "";
var result = [];
var tr = document.querySelectorAll('table tr');
for (var i = 0; i < tr.length; i++) {
var td = tr[i].querySelectorAll('td');
if (td.length === 1) currentCountry = td[0].innerHTML;
else if (td.length > 1) {
for (var a = 0; a < td.length; a++) {
result.push({country: currentCountry,city: td[a].innerHTML});
}
}
}
console.log(result);
<table>
<tbody>
<tr>
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr>
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
var country = null, result = [];
var tds = Array.from(document.querySelectorAll("#myTable tbody tr td"));
for (var i = 0; i < tds.length; i++) {
let item = tds[i];
if (item.getAttribute("colspan") == "3") {
country = item.innerText;
continue;
}
result.push({ country: country, city: item.innerText });
}
console.log(result);
<table id="myTable">
<tbody>
<tr>
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr>
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
Using reduce
const items = document.querySelectorAll('table tbody td')
const results = [...items].reduce((allItems, item)=>{
if(item.getAttribute('colspan') === '3'){
allItems['country'] = item.textContent
return allItems
}
allItems.push({country: allItems['country'],city:item.textContent})
return allItems
},[])
You need to assign all <tr> which contain country names a special class. Then use querySelectorAll and use forEach loop.
const tr = document.querySelectorAll('tr');
const arr = []
let count = '';
tr.forEach(x => {
if(x.classList.contains('head')){
count = x.children[0].innerHTML
}
else{
let child = [...x.querySelectorAll('td')]
arr.push(...child.map(a => ({country:count,city:a.innerHTML})))
}
})
console.log(arr)
<table>
<tbody>
<tr class="head">
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr class="head" >
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
Not that elegant, but to me slightly more comprehensive (while being the fastest for larger input data samples) reduce() solution:
const result = [...document.getElementsByTagName('td')].reduce((res, item) => (item.getAttribute('colspan') == 3 ? res.country = item.textContent : res.obj = [...(res.obj || []), {country: res.country, city: item.textContent}], res), {}).obj;
console.log(result);
<table>
<tbody>
<tr>
<td colspan="3">Canada</td>
</tr>
<tr>
<td>Toronto</td>
<td>Montreal</td>
<td>Vancouver</td>
</tr>
<tr>
<td colspan="3">USA</td>
</tr>
<tr>
<td>New York</td>
<td>Chicago</td>
<td>Boston</td>
</tr>
<tr>
<td>Washington</td>
<td>Detroit</td>
<td>Los Angeles</td>
</tr>
</tbody>
</table>
I want to make an array of all numbers. Here is my code:
$(function(){ // dom ready
$('tr').each(function() {
var nums = $(this).map(function() {
return (this).find('td:nth-child(3)').text()
}).get();
console.log(nums);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td>760</td>
<td>John</td>
</tr>
<tr>
<td>532</td>
<td>Peter</td>
</tr>
<tr>
<td>201</td>
<td>Martin</td>
</tr>
<tr>
<td>12</td>
<td>Barman</td>
</tr>
</tbody>
</table>
What's wrong with it and how can I fix it?
Firstly you're missing a $ prefix on the (this) in the map() handler. Then to get the numbers use :nth-child(1), not :nth-child(3). Also you can just use map() directly, no need for each(). Finally, assuming you want the values to actually be numerical and not strings, use parseInt() on them, like this:
$(function() { // dom ready
var nums = $('tr').map(function() {
return parseInt($(this).find('td:nth-child(1)').text(), 10)
}).get();
console.log(nums);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td>760</td>
<td>John</td>
</tr>
<tr>
<td>532</td>
<td>Peter</td>
</tr>
<tr>
<td>201</td>
<td>Martin</td>
</tr>
<tr>
<td>12</td>
<td>Barman</td>
</tr>
</tbody>
</table>
Small Mistake,
change return (this).find('td:nth-child(3)') to return $(this).find('td:nth-child(1)')
$(function(){ // dom ready
$('tr').each(function() {
var nums = $(this).map(function() {
return $(this).find('td:nth-child(1)').text()
}).get();
console.log(nums);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td>760</td>
<td>John</td>
</tr>
<tr>
<td>532</td>
<td>Peter</td>
</tr>
<tr>
<td>201</td>
<td>Martin</td>
</tr>
<tr>
<td>12</td>
<td>Barman</td>
</tr>
</tbody>
</table>
Change like this parseInt($(this).find('td:nth-child(1)').text()).parseInt() its used to change the string to number
Whats Wrong?
Missing $in return statement
And numbers column is a first cell find('td:nth-child(1)')
But you are trying to create a array with 3rd cell .but is not there.only two column is available
$(function(){ // dom ready
$('tr').each(function() {
var nums = $(this).map(function() {
return parseInt($(this).find('td:nth-child(1)').text())
}).get();
console.log(nums);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td>760</td>
<td>John</td>
</tr>
<tr>
<td>532</td>
<td>Peter</td>
</tr>
<tr>
<td>201</td>
<td>Martin</td>
</tr>
<tr>
<td>12</td>
<td>Barman</td>
</tr>
</tbody>
</table>
I think this is ist a 'lighter' version:
var nums=[];
$('td:nth-child(1)').each(function(){
nums.push(parseInt(this.innerHTML));
});
console.log(nums);
//[760, 532, 201, 12]
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a table with tbodies. I want to create a array with the values in the first td of with tbody. How can I do that?
My html:
<table id="myTable">
<thead>
<tr>
<th>Test1</th>
</tr>
</thead>
<tbody>
<tr>
<td>Val1</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val2</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val3</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val4</td>
<td></td>
<td></td>
</tr>
</tbody>
My array should have the values: Val1, Val2, Val3, Val4
There is a function called getElementsByTagName(tagName) which returns an array of elements.
Something like this should work:
var array = []; //your array
var rows = document.getElementById("myTable").getElementsByTagName("tr");
for(var i = 0; i < rows.length; i++) {
array[i] = rows[i].getElementsByTagName("td")[0].innerHTML;
}
Try this:
var bodies = document.getElementById("myTable").tBodies;
var items = [];
for (var i = 0; i < bodies.length; i++)
items.push(bodies[i].getElementsByTagName("td")[0].innerText);
alert(items);
<table id="myTable">
<thead>
<tr>
<th>Test1</th>
</tr>
</thead>
<tbody>
<tr>
<td>Val1</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val2</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val3</td>
<td></td>
<td></td>
</tr>
</tbody>
<tbody>
<tr>
<td>Val4</td>
<td></td>
<td></td>
</tr>
</tbody>
Try to use $(selector).map() at this context,
var x = $("#myTable > tbody > tr > td:first-child").map(function(){
return this.textContent;
}).get().join();
console.log(x); //Val1, Val2, Val3, Val4
DEMO
A pure javascript version by using Array.prototype.map() along with querySelectorAll(),
var x = [].map.bind(
document.querySelectorAll("#myTable > tbody > tr > td:first-child"),function(elem){
return elem.textContent;
})();
console.log(x); //Val1, Val2, Val3, Val4
DEMO
you can do this with jQuery:
var myArray=[];
$('#myTable tbody tr td:nth-child(1)').each(function(){
myArray.push($(this).text());
});
Just use the native javascript [Element].getElementsByTagName([TagName])[index] method.
// Get the array of <tbody> elements in the document.
tbodies = document.getElementsByTagName("tbody");
values = [];
// Loop through the array of <tbody> elements.
for(var index = 0; index < tbodies.length; index++) {
// Add the value of the <td> element to the array of values.
values += tbodies[index].firstChild.firstChild.nodeValue;
}
The line values += tbodies[index].firstChild.firstChild.nodeValue gets each <tbody> element and finds it's firstChild (the <tr> element) and finds the <tr> element's firstChild (the <td> element) and then finds the text that it contains (.nodeValue).
The values array ends up with resulting value of ["Val1","Val2","Val3","Val4"].
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.