Reputation: 34519
Say you have a class Person
. Its definition is the following:
function Person(name, age) {
this.name = name;
this.age = age;
}
Now say you have an array of Persons
(or people) and you want to find the oldest one. In C#, you would do this via extension methods:
Person Oldest(this IEnumerable<Person> people) =>
people.OrderByDescending(p => p.Age).First();
// Usage:
var elder = people.Oldest();
What's the equivalent in JavaScript? So far, this is all I was able to come up with:
Array.prototype.oldest = function() {
return this.slice().sort(function(left, right) {
return left.age - right.age;
}).pop();
};
This works well and has the same syntax as C#, but has a few drawbacks:
1) It modifies the prototype of a builtin, which could potentially lead to conflicts with other libraries that do so.
2) It is not type-safe: it's easy to accidentally call on an array that doesn't contain people, which could lead to a runtime error.
Alternatively, you could define it like this:
function oldest(people) {
// ...
}
But then you would have to call it like oldest(people)
instead of people.oldest()
, which would hamper readability.
Is there any type-safe, syntactically "nice" way to do this in JavaScript that I'm missing?
Upvotes: 0
Views: 387
Reputation: 386654
Another approach with a prototype of sorting. Person.prototype.ageCompare
checks if the parameter is instance of Person
and throws an error if not.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.ageCompare = function (b) {
if (!(b instanceof Person)) {
throw 'b is no instance of Person!';
}
return this.age - b.age;
};
var peoples = [new Person('Tim', 31), new Person('Jane', 32), new Person('Frank', 28), new Person('Sue', 31), new Person('Baby', 2)];
peoples.sort(function (a, b) { return a.ageCompare(b); });
document.write('<pre>' + JSON.stringify(peoples, 0, 4) + '</pre>');
Upvotes: 0
Reputation: 3030
Because js isn't type safe, you're better off implementing that method as oldest(people)
...
Just think of it as a decorator pattern - a legit approach to projecting functionality into an unknown set of related entities.
Then - you can do type-checking within your function.
You could namespace it to make the syntax more "obvious" if that's your concern I guess:
personList(persons).getOldest()
As one objective-c-ish example.
Then, you can add your type-specific checking to the creator for personList:
function personList(persons) {
//iterate over persons and make sure
//it's valid. return undefined or {} if not.
return {
getOldest: function() {
// insert clever code here ...
persons.slice().dice().puree();
},
getYoungest: function() {
//even more clever code here ...
}
}
}
With that approach, the code
PersonList(cats).getOldest()
With throw a 'getOldest undefined' error -- which makes it easy to spot the problem when you have 11,000,000 lines of js code to sort through.
It has the added advantage of making your c# inner-child feel more at home (with a js file called "ArrayExtensions.Persons.js" in your project folder). Feels like "chicken soup for the c# soul," right?
Upvotes: 2
Reputation: 288250
You can try different approaches:
If you only have one array of Person
s in your program, you can add the method as an own property of the array.
var people = [person1, person2];
people.oldest = function() {
// Find the oldest person in this array
};
// ... later
people.oldest();
You can define your method as an static property of Person
, and pass the array of Person
s as an argument:
Person.oldest = function(people) {
// Find the oldest person in an array
};
var people = [person1, person2];
Person.oldest(people);
Extend Array
(requires ES6)
class People extends Array {
oldest() {
// Find the oldest person in a People collection
}
}
var people = new People();
people.push(person1, person2);
people.oldest();
Upvotes: 1