Array.prototype.filter() nie zwraca danych

0
var RandomUser = (() => {
    var userList = [];

    function getUser() {
        return userList;
    }

    function addUser(data) {
        let user = {};
        user.first = data.name.first;
        user.last = data.name.last;
        user.born = Number(data.dob.split("-")[0]);
        user.died = Number(data.registered.split("-")[0]);
        userList.push(user);
    }

    function init(opts) {
        $.ajax({
            url: "https://randomuser.me/api",
            dataType: "json",
            data: {
                results: opts.items
            },
            success: (data) => {
                for (let i = 0; i < data.results.length; i++) {
                    addUser(data.results[i]);
                }
            }
        })
    }

    return {
        init: init,
        getUser: getUser,
        addUser: addUser
    };
})()

RandomUser.init({
    items: 15
});

var investor = RandomUser.getUser();
console.log(investor);

var filtered = investor.filter((user) => (user.born >= 1900 && user.born <= 1960));

console.log(filtered);

Mam taki oto prosty kod, pobieram dane ze strony generującej losowe dane, przebieram sobie dane które potrzebuje i trzymam w "fabryce"? Ogólnie rzecz biorąc to ćwiczę wzorce projektowe więc i samo pobieranie danych losowych chciałem ułatwić i sprowadzić do jednej funkcji czyli po przez podanie jednego parametru do funkcji ograniczyć ilość danych pobieranych z zewnętrznej strony, czyli coś na zasadzie "facade"? dosyć prostej. Generalnie mam problem z danymi które filtruje, otóż napisałem sobie filtr który ma zwracać tablice obiektów, które mieszczą się w określonym tam przedziale.
Niestety mi to nie działa, nawet gdy w mojej callbackFn zwrócę samo true to i tak te dane nie są zwracane. Nie wiem, myślałem, że nie jestem, aż tak głupi na to ale mnie to trochę przerosło. Podejrzewam że to problem z moimi danymi które są pobierane z zewnętrznego źródła i przechowywane w tej fabryce jeśli tak to można nazwać, ale przecież później sobie zwracam to do już do zmiennej spoza tej fabryki i wyświetla to poprawnie, ale cóż po moim filtrze brakuje danych zwracanych.

Jeśli ktoś jest obeznany to proszę o poświęcenie paru chwil i ewentualnie nakreśleniu większych błędów jakie robie.

**Edit1 **
Sprawdziłem co wyskoczy dla investor[0] i mam undefined, pytanie dlaczego?

2

Pobierasz użytkowników asynchronicznie i o tym zapomniałeś.

// tutaj zaczyna się wykonywać ajax request, który załóżmy trwa 5 sekund.
RandomUser.init({
    items: 15
});

// javascript idzie dalej
// investor jest pusty bo do tablicy userList nie zostało jeszcze nic dodane.
var investor = RandomUser.getUser();
// więc filtered też jest puste
var filtered = investor.filter((user) => (user.born >= 1900 && user.born <= 1960));

// 5 sekund później...
var investor = RandomUser.getUser();
var filtered = investor.filter((user) => (user.born >= 1900 && user.born <= 1960));
console.log(filtered);

Uwagi

  1. Funkcja getUser jest myląca, bo zwraca tablicę użytkowników, a nie pojedyńczego usera. Powinieneś zmienić albo nazwę, albo zwracaną wartość.
  2. Jeżeli się da spróbuj unikać imperatywnych pętli i zamiast tego używać forEach, map, filter, reduce:
   // for (var i = 0; i < data.results.length; i++) {
   //   addUser(data.results[i]);
   // }
   // czyż nie prościej? :D
   data.results.forEach((user) => addUser(user));

   // Ponieważ do funkcji foreach jest przekazywany element user
   // ktory jest rowniez argumentem naszej funkcji to możemy to zapisać
   // użwając tzw. point free notation
   data.results.forEach(addUser); 
  1. Do rozwiązania swojego problemu z asynchronicznym pobieraniem uzytkowników możesz użyc promises.
 function init(opts) {
     var d = $.Deferred();
     // ....
       success: (data) => {
         data.results.forEach(addUser);
         d.resolve(userList);
       }
     // ...

     return d.promise();
 }

 // i sposób użycia ...
 RandomUser.init({
    items: 15
 }).then(investor => {
    filtered = investor.filter((user) => (user.born >= 1900 && user.born <= 1960));
    console.log(filtered);
 })
  1. Warto wiedzieć, że javascript zwraca referencje do wartości (a raczej kopię: Does Javascript pass by reference?), więc Twoja metoda getUsers ma pewien efekt uboczny. Nie ma co sie nad tym pochylać, ale jak już powiedziałem, warto wiedziec :)
  var foo = (() => {
      var bar = [1, 2, 3];

      return {
        getBar: function() {
          return bar;
        }
      }
  })();

  var bar = foo.getBar();

  console.log(bar) // [1, 2, 3]

  bar.pop();

  console.log(bar); // [1, 2]
  console.log(foo.getBar()); // [1, 2] - Ooops! Zmodyfiowaliśmy zmienną wewnątrz foo!! A przecież public api udostępnia tylko pobieranie!

  var foo = (() => {
      var bar = [1, 2, 3];

      return {
        getBar: function() {
          // Tworzymy kopię tablicy i zwracamy tą kopię
          return [...bar];
        }
      }
  })();

  var bar = foo.getBar();

  console.log(bar) // [1, 2, 3]

  bar.pop();
  console.log(bar); // [1, 2]
  console.log(foo.getBar()); // [1, 2, 3]
0

Super, powoli zaczyna to mieć sens, zagmatwane to wszystko :(, więc w jakiś sposób muszę poczekać aż moje żądanie zwróci wartości, tylko że żądanie się wykonuje w fabryce a funkcje chcę wykonać już poza.. Czy setTimeout to jakieś sensowe rozwiązanie?

0

Tu masz prostsze rozwiązanie (bo nie wiem po co tam @Desu wlasny deffered tworzy, skoro $.ajax go zwraca:

const RandomUsers = (() => {
  let usersList = [];

  function getUsers() {
    return usersList;
  }

  function addUser({ name, dob, registered }) {
    usersList.push({
      first: name.first,
      last: name.last,
      born: Number(dob.split("-")[0]),
      died: Number(registered.split("-")[0]),
    });
  }

  function init(opts) {
    return $
      .ajax({
        url: "https://randomuser.me/api",
        dataType: "json",
        data: {
          results: opts.items
        },
      })
      .then(data => data.results.forEach(addUser))
    ;
  }

  return {
    init,
    getUsers,
    addUser,
  };
})()
 
RandomUsers
  .init({ items: 15 })
  .then(() => RandomUsers.getUsers())
  .then(users => users.filter(user => user.born >= 1900 && user.born <= 1960))
  .then(console.log)
;

CodePen: http://codepen.io/anon/pen/QdLGGr?editors=0012

Ogólnie pewnie bym to jeszcze inaczej zrobił, bez tej dodatkowej zmiennej let usersList, no ale nie bdę mieszał.

1 użytkowników online, w tym zalogowanych: 0, gości: 1