4

I am attempting to use the Wikipedia API to retrieve article titles and snippets of the article's text. But when I try to access those properties, I am getting the error "Cannot read property of undefined."

Here is my JSON response:

{
    "batchcomplete": "",
    "continue": {
        "gsroffset": 10,
        "continue": "gsroffset||"
    },
    "query": {
        "pages": {
            "13834": {
                "pageid": 13834,
                "ns": 0,
                "title": "\"Hello, World!\" program",
                "index": 6,
                "extract": "<p>A <b>\"Hello, World!\" program</b> is a computer program that outputs or displays \"Hello, World!\" to a user. Being a very simple program in most programming languages, it is often used to illustrate the</p>..."
            },
            "6710844": {
                "pageid": 6710844,
                "ns": 0,
                "title": "Hello",
                "index": 1,
                "extract": "<p><i><b>Hello</b></i> is a salutation or greeting in the English language. It is first attested in writing from 1826.</p>..."
            },
            "1122016": {
                "pageid": 1122016,
                "ns": 0,
                "title": "Hello! (magazine)",
                "index": 7,
                "extract": "<p><i><b>Hello</b></i> (stylised as <i><b>HELLO!</b></i>) is a weekly magazine specialising in celebrity news and human-interest stories, published in the United Kingdom since 1988. It is the United Kingdom</p>..."
            }
        }
    }
}

I have tried a couple different ways of writing the code. For example, this works (logs the pages as an object in the console):

console.log(response.query.pages);

But this returns the error I wrote above ("Cannot read property of undefined"):

console.log(response.query.pages[0].title);

Any suggestions on how to access the attributes "title" and "extract" would be appreciated. Thanks.

jdunlop
  • 181
  • 1
  • 9
Scott
  • 1,207
  • 2
  • 15
  • 38

3 Answers3

6

That's because pages is not an array; it's an object where the keys are the ids. So you need to do:

console.log(response.query.pages[1122016].title);

This will work. If you want the "first" page, for instance, then

let pages = response.query.pages;
console.log(pages[Object.keys(pages)[0]].title);

Note that I'm not sure if the order of the keys in JS objects is guaranteed.

If you want to iterate over the pages, do

let pages = response.query.pages;
Object.keys(pages).forEach(id => {
    let page = pages[id];
    console.log(page.title, page.foo);
});
Luan Nico
  • 5,376
  • 2
  • 30
  • 60
  • 1
    To get an array of them: `Object.keys(pages).map(id => pages[id])` – T.J. Crowder Apr 21 '17 at 17:20
  • 1
    *"Note that I'm sure if the order of the keys in JS objects is guaranteed."* Not with `Object.keys` or `for-in`, no. It is with `Object.getOwnPropertyNames`. (In this case, with `Object.getOwnPropertyNames`, it would be in numeric order ascending, as those keys fit the definition of an array index [yes, even though it's not an array].) – T.J. Crowder Apr 21 '17 at 17:21
  • @RickHitchcock: Not with these keys, they fit the "array index" definition. Implementations have historically treated those differently from others. – T.J. Crowder Apr 21 '17 at 17:21
  • 1
    Yes, I meant *not* sure updated above ;) Thanks for the insight. – Luan Nico Apr 21 '17 at 17:22
  • Thanks for the answer and explanation! – Scott Apr 21 '17 at 17:31
1

Special Case: Working with Asynchronous Calls

Howdy fellow devs,

If you're checking out this thread because you're working with a framework like React, or some other framework that has you using a development server (e.g. using npm start or something similar), your dev server may be crashing when you try to do something like console.log(response.foo.bar) before it refreshes on data reload.

Specifically, my dev server was crashing with the Cannot read property 'bar' of undefined type message, and I was like, "what the heck is going on here!?". Solution: put that baby in a try/catch block:

try { 
    console.log(rate['bar'].rate)
  } catch (error) {
    console.log(error)
  }

Why? If your App has a default state (even an empty array, for example), then it tries to console.log the response before the data has been received from the remote source, it will successfully log your empty state, but if you try to reference parts of the object you're expecting to receive from the remote source in your console.log or whatever else, the dev server will be trying to reference something not there on initial load and crash before it has a chance to reference it when it's actually received from the remote source via API or whatever.

Hope this helps someone!

Davis Jones
  • 1,504
  • 3
  • 17
  • 25
-2

I'm not sure which language you're using to parse the JSON (looks like Javascript from console.log?) but the issue is that query.pages is a dictionary, not an array, so it can't be iterated by index, only by key.

So you want something like (pseudocode):

for (key in response.query.keys)
{
    console.log(response.query[key].title);
}
jdunlop
  • 181
  • 1
  • 9