DEV Community

Cover image for Understanding JavaScript Iteration with C#
Sung M. Kim
Sung M. Kim

Posted on • Originally published at slightedgecoder.com on

Understanding JavaScript Iteration with C#

As I was chatting, someone from Coding Blocks slack channel (#javascript) has asked a question regarding a code snippet

His question was “Kinda lost in the [].filter.call , etc part.”

I will show you a C# code snippet to explain “why” someone used call instead of directly calling filter.

🔨 Code Breakdown – uniqueInOrder

uniqueInOrder returns a distinct list of array for an order array with duplicate values.

Given an array [1, 1, 2, 2, 2, 3, 3, 3, 3], uniqueInOrder returns [1, 2, 3].

Click on "Run" to see the result.

const arr = [1, 1, 2, 2, 2, 3, 3, 3, 3]; var uniqueInOrder = function(iterable) { return [].filter.call(iterable, function(a, i) { return iterable[i - 1] !== a; }); }; console.log(uniqueInOrder(arr));

But couldn’t you have just used Array#filter?
var arr = [1, 1, 2, 2, 2, 3, 3, 3, 3]; var uniqueInOrder = function(iterable) { return iterable.filter(function(a, i) { return iterable[i - 1] !== a; }); }; console.log(uniqueInOrder(arr));

Yes it works but [].filter.call can handle objects not derived from Array.

🧐 What do you mean?

Some objects are iterable and not derived from Array.

Refer to How I learned to Stop Looping and Love the Iterator post by Kushan for details on iterable/iterators.

The (notoriously) notable one is NodeList, which is returned by document.querySelectorAll.

🤷 So What?

filter is defined by Array prototype thus an object calling “filter” should implement an Array prototype.

But NodeList doesn’t implement Array prototype, so you can’t call filter on it even though it’s iterable.

[].filter.call lets you use the filter method without having to convert non-Array sequences into an array.
Therefore making uniqueInOrder method more generic.

Here is an example of working with NodeList with Array#filter.

  • document.querySelectorAll('a') returns an object of type NodeList.
  • When you try to call filter directly, it fails.
  • You can get around it by using a spread syntax.
  • And [].filter.call works as well.

document queryselctorall example

🐳 C# Analogy

If you use C#, you might have run into IEnumerable<T>.

It’s an interface, which enables implementing class to be iterable.

Let’s see two methods that accepts an iterable object of type string and prints each element.

PrintWords on .NET Fiddle

If you were to pass wordArray to ListPrintWords, it will fail to compile while GenericPrintWords is happy to work with it.

Argument Error

😀 What did we learn?

So [].filter.call can be used to deal with any iterable objects that do not inherit Array prototypes.

And it’s roughly equivalent to dealing with objects implementing IEnumerable interface in .NET, thus enabling methods accept any kind of generic iterable sequences.

👋 Parting Words

I’ve had hard time understanding the WHY of such a method initially.

I am able to see more when I make analogies to languages I am used to (C#).

Functional Programming (FP) in JavaScript is being adopted nowadays (Redux, Functional Light JS by Kyle Simpson), thus started learning Elixir to see WHY JavaScript community is raving about FP.

I’d love your feedback/errata. So leave a comment below or send me a twit 🙂

The post Understanding JavaScript Iteration with C# appeared first on Slight Edge Coder.

Oldest comments (9)

Collapse
 
nickytonline profile image
Nick Taylor

Thanks for the post Sung. For array-like objects, like NodeList, there are new Array methods, such as Array.from. Here's some handy tricks, including one to get unique values.

Collapse
 
dance2die profile image
Sung M. Kim

Wow 😮 Nick.
Thank you for the treasure 💎👑 trove 🗃 .

Collapse
 
miffpengi profile image
Miff

My question is why do we have to trick an array function into operating on something else. Can't you just turn whatever iterable object you get into an array with Array.from?

return Array.from(iterable).call(function (a, i) { return iterable[i - 1] !== a });

Collapse
 
dance2die profile image
Sung M. Kim

Hi Miff.

ES6 makes it much easier to turn iterables into arrays using Array.from or spread syntax.

const iterable = document.querySelectAll('a');
Array.from(iterable).filter((a, i) => iterable[i - 1] !== a);
[...iterable].filter((a, i) => iterable[i - 1] !== a );

If you were to use only ES5 (for a personal preference or due to a business requirement) [].filter.call will come in handy.

At this point I am not sure about performance implications though.

Collapse
 
goaty92 profile image
goaty92 • Edited

My bet is that performance implication will be on the bad side.
Both Array.from and spread syntax create a new array meaning they allocate extra memory. If your array size is small it 'might' be ok, but it might be a problem if you're processing large arrays

Thread Thread
 
dance2die profile image
Sung M. Kim

Sounds like a valid point there.

I'd still use a spray syntax over abstract [].filter.call as to promote readability (unless serious performance is gained)

Thread Thread
 
goaty92 profile image
goaty92

Sure, and if you use Typescript, with spray syntax the compiler can know about the type of the array and provide typing information.

If you're concerned with performance then manually iterating through the array is always the best option.

Collapse
 
vberlier profile image
Valentin Berlier

In my opinion, [].filter is kind of cryptic. You're creating a new empty array and then discarding it just to be able to use one of its methods on another object. If you want to access prototype methods in general, get them from the actual prototype object:

Array.prototype.filter.call(iterable, condition)

It's a little longer but it doesn't look weird, doesn't create an unnecessary array, and anyone reading the code can see that the method I'm using comes from the Array prototype. This approach also works for every kind of prototype, not just arrays.

Collapse
 
dance2die profile image
Sung M. Kim

Thanks Valentin.

I also think that [].filter.call is just a clever trick, which can confuse people.

Clever doesn't necessarily make it a good or readable code and I do agree with your opinion it being "cryptic".