2
\$\begingroup\$

Create a user class with id, name, age. Write a function to get users older than age 20. Group all users with same age and increment users age by 3 Is there a cleaner way to write this? Any comments are appreciated.

const userList=[];
const userAboveTwenty= [];

class User {
  constructor(name,id,age){
    this.name=name;
    this.id=id;
    this.age=age;
  }

  addUser(user){
    userList.push(user);
  }

  getUserListAboveTwenty(list){
  userAboveTwenty.push(...list.filter(x=>x.age>20));
}

addAge(user){
  return user.age = user.age+3;
}
}


let user1 = new User('user1',1,12);
let user2 = new User('user2',2,18);
let user3 = new User('user3',3,21);
let user4 = new User('user4',4,22);

user1.addUser(user1);
user2.addUser(user2);
user3.addUser(user3);
user4.addUser(user4);
user1.addAge(user1);
user1.getUserListAboveTwenty(userList);
user1.addAge(user1);
```
\$\endgroup\$
  • \$\begingroup\$ Sure. I am fairly new to this. Will add beginner tag moving forward. Thanks \$\endgroup\$ – RamaM Jul 5 at 11:06
3
\$\begingroup\$

Your User class should only be dealing with things about that user, not about how you store it in a list. You are also referring to a variable outside your class, so you are locked to this, which makes it less reusable, which is one of points of classes in the first place. So get rid of addUser and getUserListAboveTwenty from your class.

You don't need to pass the user to your functions, you can access it with this, like you've done in the constructor. You can also make your addAge function more general by adding age based on a parameter.

addAge(age){
  this.age += age;
}

If you just need to store your users in a list, an array is just fine. But if you want more functionality you could make a UserList class.

class UserList {
    constructor(users = []) {
        this.list = users;
    }

    addUser(user) {
        this.list.push(user);
    }

    getUserById(id) {
        return this.list.find(user => user.id == id);
    }

    getUsersAboveTwenty() {
        return new UserList(this.list.filter(user => user.age > 20));
    }
}

I've added an additional function to return a user from an id. I also made getUsersAboveTwenty return a new UserList. You can add more if you need it (A way to add more users at the same time would be useful). Full code:

class User {
    constructor(name, id, age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    addAge(age){
        this.age += age;
    }
}

class UserList {
    constructor(users = []) {
        this.list = users;
    }

    addUser(user) {
        this.list.push(user);
    }

    getUserById(id) {
        return this.list.find(user => user.id == id);
    }

    getUsersAboveTwenty() {
        return new UserList(this.list.filter(user => user.age > 20));
    }
}

let userList = new UserList();

userList.addUser(new User('user1',1,12));
userList.addUser(new User('user2',2,18));
userList.addUser(new User('user3',3,21));
userList.addUser(new User('user4',4,22));

userList.getUserById(1).addAge(3);
userList.getUsersAboveTwenty();
userList.getUserById(1).addAge(3);
\$\endgroup\$
  • \$\begingroup\$ Thank you. This was helpful. \$\endgroup\$ – RamaM Jul 5 at 11:11
3
\$\begingroup\$

Consider a functional approach

I have been coding Java for a long time before I started with Javascript so I understand the urge to write Object Oriented code, create classes etc. However, why don't you try a functional approach for a change. I bet that once you get the hang of it, you will not want to go back. I know I don't.

Javascript already had some great functional features, but since we got arrow functions it's just become a dream. Just look how much boilerplate we can get rid of once we accept that we don't need user objects to be an instance of some class. In fact, them being just plain objects is a big plus. It makes them easily serializable, To JSON for example.

const users = [];

const byId = (id) => (user) => user.id === id
const olderThan = (age) => (user) => user.age > age
const makeOlder = (user, years) => user.age += years

let user1 = { name: 'user1', id: 1, age: 12 }
let user2 = { name: 'user2', id: 2, age: 18 }
let user3 = { name: 'user3', id: 3, age: 21 }
let user4 = { name: 'user4', id: 4, age: 22 }

users.push(
  user1, 
  user2, 
  user3, 
  user4
)

console.info('users', users)
console.info('id=2', users.filter(byId(2)))

makeOlder(user1, 3 /* years */)
console.info('user1.age', user1.age)

const above20 = users.filter(olderThan(20))
console.info('age>20', above20)

Note about the above code that there is less of it, it's using almost only standard constructs (we are not introducing custom list classes etc) and it is in fact more readable. Also, you'll find it is much more reusable because we create small, separate functions that work on any object instead of big classes with many members.

You can do OO in Javascript

If you really want object oriented code, the language has enough to make you happy. Study how to do prototypal inheritance and information hiding / private members by learning from one of the masters: Douglas Crockford.

You just don't need it

Then... once you've learned it all... liberate yourself from it and discover... You don't need it!

For me, one of the biggest eye openers was how arrays are implemented in Javascript. Did you know, that all the Array methods need to do their work, is a length property? In fact, you can turn any object into an array-like object and have all array methods work, just by adding a length. Let me prove it to you:

var SuperArray = {}                         // omg, just a plain old object! 
SuperArray.length = 0                       // surely, this won't work??
SuperArray.push = Array.prototype.push      // what?
SuperArray.join = Array.prototype.join      // are..you doing???

// Done!
// No way!
// Way  :)

SuperArray.push('no way!')
SuperArray.push('way!')
SuperArray.push('Are you kidding me?')
SuperArray.push('No i\'m not.')
SuperArray.push('You DO NOT NEED CLASSES in Javascript.')
SuperArray.push('It\'s way cooler than that!')

console.info('SuperArray.length', SuperArray.length)
console.info('SuperArray (joined)', SuperArray.join('\n'))

This for me is what is so incredibly cool (and powerful) about Javascript. Consider trying to figure out why and how this works. And maybe see if you can put it into practice.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the comments. I am very comfortable with the functional approach. But when it comes to implementing the same using classes, it gets too confusing.Thanks for the link, the post seems promising. \$\endgroup\$ – RamaM Jul 5 at 22:14
  • \$\begingroup\$ In your code example, you are using ES6 classes. The important thing to understand about those is that they are syntax sugar. You can build those classes using ES5 and the prototype chain (in fact, behind the curtains, that's still what ES6 classes are doing) but it's cumbersome. But I feel trying to build a 'class' by hand using ES5 syntax only might help you learn how it works. Do you have experience with other OO languages, if you do not mind me asking? \$\endgroup\$ – Stijn de Witt Jul 5 at 22:18
  • \$\begingroup\$ Do you have experience with other OO languages, if you do not mind me asking --> I am in the process of learning it. Don't have prior experience with other languages. \$\endgroup\$ – RamaM Jul 5 at 22:22
  • \$\begingroup\$ @RamaM Ha, I guess in this case, that is a good thing. The way Javascript implements OO is different from other languages. I have a great deal of experience with Java which is a strong-typed OO language. But for me, OO is sort of 'over'. I have gotten to the point where I try to avoid it. Trying to build a class model is like making a folder tree. You will never decide whether it should be CustomerX/Invoices/2019 or Invoices/CustomerX/2019 or 2019/CustomerX/Invoices or... \$\endgroup\$ – Stijn de Witt Jul 5 at 22:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.