Leafyshark
Leafyshark

Reputation: 395

Why am I getting 'cannot read property 'filter' of undefined' when the array being filtered contains JSON data?

The following code

const endpoint = 'https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json';
const universities = [];

fetch(endpoint)
    .then(results => results.json())

    .then(data => universities.push(...data));

console.log(universities);

function findMatches(wordToMatch, universities) {
    return universities.filter(uni => {
        const regex = new RegExp(wordToMatch, 'gi');
        return uni.name.match(regex)
    })
}

export default findMatches;

returns the error below

'Uncaught TypeError: Cannot read property 'filter' of undefined'

I can log the data using console.log(universities). So why can't I filter through it? FYI the data is an array of objects. Any help is massivaly appreciated. Thank you.

Upvotes: 1

Views: 11437

Answers (2)

Leafyshark
Leafyshark

Reputation: 395

I just want to let everybody know I finally got things working. I had to install babel-polyfill and babel-preset-env and add to webpack to get UglifyJS to work with async await and to optimise bundle size.

I had to use async await instead of regular promises to get the HTML to render into the DOM for some reason, not sure why. Anyway here is the code that finally worked as expected:

UniFetch.js

const endpoint = 'https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json';

const universities = [];

const promise = fetch(endpoint)
    .then(blob => blob.json())
    .then(data => universities.push(...data.slice(8286, 8456)));

function findMatches(wordToMatch) {
    return promise.then(() => universities.filter(uni => {
        const regex = new RegExp(wordToMatch, 'gi');
        return uni.name.match(regex)
    }));
}

async function displayMatches() {
    searchResults.innerHTML = await findMatches(this.value)
        .then(arr => arr.map(uni => {
        return `
            <li>${uni.name}</li>
        `
    }));
}

const searchInput = document.querySelector("input[name='unisearch']");
const searchResults = document.querySelector('.unisearch__results');

searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);

export default findMatches

App.js

import FindMatches from '../UniFetch.js'

FindMatches()

Hope this helps some people to implement a typeahead, autocomplete API fetch.

Upvotes: 1

Christian Santos
Christian Santos

Reputation: 5456

You need to remove universities as a parameter in your findMatches function because it is overriding the value of the local universities variable:

function findMatches(wordToMatch) {
    return universities.filter(uni => {
        const regex = new RegExp(wordToMatch, 'gi');
        return uni.name.match(regex)
    })
}

You can then proceed to use the findMatches function as follows:

findMatches("hi") // returns a filtered array

Edit:

You have a race condition in your code, wherein findMatches might be called before your fetch is complete. To fix the issue, findMatches should return a promise like so:

const endpoint = 'https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json';
const universities = [];

const promise = fetch(endpoint)
    .then(results => results.json())

    .then(data => universities.push(...data));

console.log(universities);

function findMatches(wordToMatch) {
    return promise.then(() => universities.filter(uni => {
        const regex = new RegExp(wordToMatch, 'gi');
        return uni.name.match(regex)
    }));
}

findMatches("hi").then(arr => console.log(arr));

If you're absolutely sure that findMatches will always be called after fetch is complete, you can go with the first solution. Otherwise, it is strongly recommended you go with the second solution that uses promises.

Upvotes: 3

Related Questions