Level 5 tweets.ejs .forEach fails

Troy Murray's Avatar

Troy Murray

09 Jan, 2013 04:03 PM

I'm trying to re-create the example shown in the Node.js level 5 slides to pull the tweets for a user. However I'm running into a problem when I'm trying to loop over each of the tweets that are returned.

app.js

var url = require('url');
var request = require('request');
var express = require('express');
var ejs = require('ejs');

var app = express();

app.get('/', function(req, response) {
  options = {
    protocol: "http:",
    host: "search.twitter.com",
    pathname: '/search.json',
    query: { q: "codeschool"}
  };

  var searchURL = url.format(options);

  request(searchURL, function(err, res, body) {
    var tweets = JSON.parse(body);
    response.render('tweets.ejs', {tweets: tweets, name: 'codeschool'});
  });
});
  
app.get('/:username', function(req, response) {
  var username = req.params.username;
  options = {
    protocol: "http:",
    host: "search.twitter.com",
    pathname: '/search.json',
    query: { q: username}
  };

  var searchURL = url.format(options);

  request(searchURL, function(err, res, body) {
    var tweets = JSON.parse(body);
    response.render('tweets.ejs', {tweets: tweets, name: username});
  });
});
  
app.listen(8080);

tweets.ejs

<h3>Tweets for @<%= name %></h3>
<ul>
  <% tweets.forEach(function(tweet){ %>
    <li><%= tweet.text %></li>
  <% }); %>
</ul>

The error

TypeError: /node.js/views/tweets.ejs:3
    1| <h3>Tweets for @<%= name %></h3>
    2| <ul>
 >> 3|   <% tweets.forEach(function(tweet){ %>
    4|     <li><%= tweet.text %></li>
    5|   <% }); %>
    6| </ul>

Object #<Object> has no method 'forEach'...

Any ideas? From what I can tell this code is returning an object:

  request(searchURL, function(err, res, body) {
    var tweets = JSON.parse(body);
    response.render('tweets.ejs', {tweets: tweets, name: username});
  });

And objects don't have a forEach method, only arrays do.

  1. 2 Posted by Troy Murray on 10 Jan, 2013 02:55 PM

    Troy Murray's Avatar

    I've resolved this on my own. As I determined above, the JSON.parse(body) was returning an object to the variable tweets. Because objects do not have a forEach function this failed. The array of tweets was actually within the tweets object under results. The corrected and working template code is:

    <h3>Tweets for @<%= name %></h3>
    <ul>
      <% tweets.results.forEach(function(tweet){ %>
        <li><%= tweet.text %></li>
      <% }); %>
    </ul>
    
  2. Troy Murray closed this discussion on 10 Jan, 2013 02:55 PM.

  3. Adam Rensel re-opened this discussion on 10 Jan, 2013 03:14 PM

  4. Support Staff 3 Posted by Adam Rensel on 10 Jan, 2013 03:14 PM

    Adam Rensel's Avatar

    Hi Troy,
    You are correct, objects don't have the forEach method. I believe the body was expected to be an array of tweet objects when this challenge was first created. I tried hitting the twitter search api (outside of node) for my twitter name and was returned:

    {"completed_in":0.027,"max_id":287378568093257729,"max_id_str":"287378568093257729","page":1,"query":"adamrensel","refresh_url":"?since_id=287378568093257729&q=adamrensel","results":[{"created_at":"Sat, 05 Jan 2013 02:02:44 +0000","from_user":"adamrensel","from_user_id":240491140,"from_user_id_str":"240491140","from_user_name":"Adam Rensel","geo":null,"id":287378568093257729,"id_str":"287378568093257729","iso_language_code":"en","metadata":{"result_type":"recent"},"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1220532373\/0d47243_normal.jpg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1220532373\/0d47243_normal.jpg","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","text":"@olivierlacan @aimee_simone Totally guilty =\\","to_user":"olivierlacan","to_user_id":17035875,"to_user_id_str":"17035875","to_user_name":"Olivier Lacan","in_reply_to_status_id":287360200917254145,"in_reply_to_status_id_str":"287360200917254145"},{"created_at":"Sat, 05 Jan 2013 00:49:45 +0000","from_user":"olivierlacan","from_user_id":17035875,"from_user_id_str":"17035875","from_user_name":"Olivier Lacan","geo":{"coordinates":[28.540920,-81.378661],"type":"Point"},"id":287360200917254145,"id_str":"287360200917254145","iso_language_code":"en","metadata":{"result_type":"recent"},"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2875129396\/3c636343318bea79fa08feeb2e02b619_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2875129396\/3c636343318bea79fa08feeb2e02b619_normal.jpeg","source":"&lt;a href=&quot;http:\/\/tapbots.com\/software\/tweetbot\/mac&quot;&gt;Tweetbot for Mac&lt;\/a&gt;","text":"@aimee_simone Oh you bet. @adamrensel does it too. :-)","to_user":"aimee_simone","to_user_id":22692146,"to_user_id_str":"22692146","to_user_name":"Aimee Booth Simone","in_reply_to_status_id":287360128733302784,"in_reply_to_status_id_str":"287360128733302784"}],"results_per_page":15,"since_id":0,"since_id_str":"0"}
    

    Now, this is one large object, I'm not sure if twitter has changed the api since this challenge was added, but if the body returned by the request callback looks similarly, you could change this line: JSON.parse(body); to JSON.parse(body).results; which does return the array of tweets we were expecting.

    I'm only guessing though, is there any way you could log out the body in that request callback and let me know what it is returning for you? It would provide better insight into what is happening.

    -Adam R

  5. Support Staff 4 Posted by Adam Rensel on 10 Jan, 2013 03:14 PM

    Adam Rensel's Avatar

    oh bah, you beat me to it :)

    -Adam R

  6. Support Staff 5 Posted by Adam Rensel on 10 Jan, 2013 03:16 PM

    Adam Rensel's Avatar

    I'll make a note to get the slides updated to reflect the api change.

    -Adam R

  7. Adam Rensel closed this discussion on 10 Jan, 2013 03:16 PM.

  8. Troy Murray re-opened this discussion on 10 Jan, 2013 03:42 PM

  9. 6 Posted by Troy Murray on 10 Jan, 2013 03:42 PM

    Troy Murray's Avatar

    Adam,

    First, thanks for the response. Second I didn't really see that the results was the array that I needed to loop over until I used the JSON view Chrome extension (https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoi...) which made the formatting much nicer and easier to read. Last I didn't know you could chain the results attribute (would that be the correct term?) onto the JSON parse like you showed, that's pretty cool.

  10. Troy Murray closed this discussion on 10 Jan, 2013 06:28 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac