简体   繁体   中英

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

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> 

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> 

 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
 },[])

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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM