最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How do I convert data from html table into array of object using vanilla javascript - Stack Overflow

programmeradmin1浏览0评论

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 paring two arrays of objects. Unfortunately, I didn't e 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 oute 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;
    }

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 paring two arrays of objects. Unfortunately, I didn't e 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 oute 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;
    }
Share Improve this question asked May 6, 2019 at 12:48 Maksym DudykMaksym Dudyk 1,1731 gold badge16 silver badges18 bronze badges 4
  • 1 is it possible to change the markup for you? i.e. adding css classes to that table rows? – messerbill Commented May 6, 2019 at 12:53
  • Absolutely, actually, I have simplified the table, the true one is full of css' classes around both countries and cities. – Maksym Dudyk Commented May 6, 2019 at 13:08
  • does @Nina Scholz's answer fit your needs? Otherwise i can provide one using some class-selector logic – messerbill Commented May 6, 2019 at 13:13
  • @MaksymDudyk : If you happen to deal with much larger data and performance considerations do matter, you might want to check out my answer below as it gives you certain advantage in that regard, while for loop solution can be much faster on a small input table, though. – Yevhen Horbunkov Commented May 7, 2019 at 4:24
Add a ment  | 

6 Answers 6

Reset to default 3

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 prehensive (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>

发布评论

评论列表(0)

  1. 暂无评论