Pustur
Pustur

Reputation: 177

jQuery .toArray() not working as expected inside .map()

I'm having a hard time understanding the behaviour of jQuery's .toArray() in this scenario.

I have 2 <table>, one for "Participants" and one for "Winners". I want to extract some data from these tables using jQuery.

I use 2 .map() functions to transform the contents of the table into arrays/objects, have a look at the code:

HTML:

<h2>Participants</h2>
<table border="1">
  <tbody>
    <tr>
      <td>01</td>
      <td>Andrew</td>
    </tr>
    <tr>
      <td>02</td>
      <td>Julian</td>
    </tr>
    <tr>
      <td>03</td>
      <td>Matt</td>
    </tr>
    <tr>
      <td>04</td>
      <td>Sarah</td>
    </tr>
  </tbody>
</table>

<h2>Winners</h2>
<table border="1">
  <tbody>
    <tr>
      <td>01</td>
      <td>Andrew</td>
    </tr>
    <tr>
      <td>04</td>
      <td>Sarah</td>
    </tr>
  </tbody>
</table>

JavaScript:

const result = $('table')
  .map((i, table) => {
    return $(table)
      .find('tbody tr')
      .map((j, row) => {
        const $row = $(row);

        return {
          number: $row.find('td:nth-child(1)').text(),
          name: $row.find('td:nth-child(2)').text(),
        };
      })
      .toArray(); // [1]
  })
  .toArray();

console.log(result);

[1] Without the .toArray() I get an array with 2 jQuery objects, one for each table. (Great!)
But when I add .toArray() all of a sudden it returns a single array with all the objects inside, why?
Where's the separation the was there before?!

Expected output:

[
  [
    {number: "01", name: "Andrew"},
    {number: "02", name: "Julian"},
    {number: "03", name: "Matt"},
    {number: "04", name: "Sarah"}
  ],
  [
    {number: "01", name: "Andrew"},
    {number: "04", name: "Sarah"}
  ]
]

Actual output:

[
  {number: "01", name: "Andrew"},
  {number: "02", name: "Julian"},
  {number: "03", name: "Matt"},
  {number: "04", name: "Sarah"},
  {number: "01", name: "Andrew"},
  {number: "04", name: "Sarah"}
]

I made a jsfiddle so you can see for yourself: https://jsfiddle.net/93cLyq6h/22/

Upvotes: 2

Views: 284

Answers (2)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48751

The results of the $.fn.map call needs to be wrapped in square brackets [ ... ].

const result = $('table')
  .map((i, table) => [
    $(table)
      .find('tbody tr')
      .map((j, row) => [{
        number: $(row).find('td:nth-child(1)').text(),
        name: $(row).find('td:nth-child(2)').text(),
      }]).toArray()
  ]).toArray();

// Format results...
console.log(JSON.stringify(result, null, 2)
  .replace(/\{\n\s+"/g, '{ "')
  .replace(/,\n\s+"/g, ', ')
  .replace(/"\n\s+\}/g, '" }'));
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>Participants</h2>
<table border="1">
  <tbody>
    <tr>
      <td>01</td>
      <td>Andrew</td>
    </tr>
    <tr>
      <td>02</td>
      <td>Julian</td>
    </tr>
    <tr>
      <td>03</td>
      <td>Matt</td>
    </tr>
    <tr>
      <td>04</td>
      <td>Sarah</td>
    </tr>
  </tbody>
</table>

<h2>Winners</h2>
<table border="1">
  <tbody>
    <tr>
      <td>01</td>
      <td>Andrew</td>
    </tr>
    <tr>
      <td>04</td>
      <td>Sarah</td>
    </tr>
  </tbody>
</table>

Upvotes: 0

Robert Derber
Robert Derber

Reputation: 75

Your issue is actually not with .toArray() but with jquery's .map(). Here is another answer explaining this, but when the .map() parameter function returns an array, each element is inserted into the set rather than the array itself. So your outer .map() flattens the two arrays into one.

If an array is returned, the elements inside the array are inserted into the set.

Is there a jQuery map utility that doesn't automically flatten? here is something that should help you get what you're looking for.

Upvotes: 1

Related Questions