William J. Sun
William J. Sun

Reputation: 25

Different output from .map() and .each()

I cannot for the life of me figure out the difference between these two blocks of code:

var text = [];
$(".article-text p").each(function() {
    text.push( $(this).text() );
});

And

var text = $('.article-text p').map(function() {
    return $(this).text();
});

They look to me like they produce the same exact output when tested in console on the following page. However, the first one can be run by JSON.stringify, and the second cannot.

My error message in the crawler says

Error invoking user-provided 'pageFunction': Error: TypeError: JSON.stringify cannot serialize cyclic structures.

My error message in console says:

Uncaught DOMException: Blocked a frame with origin "http://yaledailynews.com" from accessing a cross-origin frame. at JSON.stringify () at :1:6

When I compare the two objects, they look exactly the same, except that the second has a context property. I have deleted this property but the errors still remain.

Upvotes: 2

Views: 101

Answers (1)

nem035
nem035

Reputation: 35481

From the docs for jQuery.map

Array-like objects — those with a .length property and a value on the .length - 1 index — must be converted to actual arrays before being passed to $.map(). The jQuery library provides $.makeArray() for such conversions.

Since $('.article-text p') will return an array-like collection (of jQuery objects), you must call $.makeArray on it:

function one() {
  var text = [];
  $(".test").each(function() {
    text.push($(this).text());
  });
  return text;
}

function two() {
  var text = $('.test').map(function() {
    return $(this).text();
  });
  return text;
}

console.log(one());
// use makeArray here
console.log($.makeArray(two()));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">A</div>
<div class="test">B</div>
<div class="test">C</div>

Docs also say

As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array.

function one() {
  var text = [];
  $(".test").each(function() {
    text.push($(this).text());
  });
  return text;
}

function two() {
  var text = $('.test').map(function() {
    return $(this).text();
  }).get(); // <--- call get here
  return text;
}

console.log(one());
console.log(two());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">A</div>
<div class="test">B</div>
<div class="test">C</div>

Upvotes: 2

Related Questions