Background
When I'm working on a software problem, I often think about the solution in terms of the language I'm most proficient. For me, that's Python. I've been coding in Python for nearly a decade and have a fluency in this language greater than any other (aside from English). But as much as I enjoy writing Python, I work within the constraints of a project's demands.
When a solution outside of Python is needed, my mental model maps the implementation between Python and the requirement in a way probably not too dissimilar to an intermediate foreign language speaker—I think in terms of my native language and translate. In developing my mental model, it helps to see where languages collide and diverge.
In this article I'll discuss what's similar and dissimilar between a couple of commonly encountered JavaScript array methods and what we can do in Python to accomplish the same outcome. It was my intention to cover several methods in a single article. But when covering just filter and find, I found I had a lot to say. To keep things concise, I'll just cover the filter and find JavaScript array methods and how they relate to their Python counterparts.
We might just have a series on our hands if I get around to writing about other methods. For now, let's jump into these two.
Filter & Find
A very common task in any programming language is locating an element, or group of elements, matching certain criteria within an iterable. In JavaScript, we have the appropriately named filter and find methods to accomplish this task. In Python, we have a filter built-in function but we don't have a find function so a little creativity is needed. We'll start with the JavaScript methods and see how these map to Python.
JavaScript
First, let's look at the implementation of find and filter in JavaScript. Both of these array methods accept as parameters callbackFn and thisArg and the callback function is called with arguments for the current element being processed, its index within the array, and the array itself.
I'll use the following array borrowed from MDN.
const inventory = [
{ name: "apples", quantity: 2 },
{ name: "bananas", quantity: 0 },
{ name: "cherries", quantity: 5 },
];
To find the "cherries" object, we need to pass a callback that returns values for each element that evaluate to true or false.
const cherries = inventory.find((item, index, arr) => {
return item.name === "cherries";
})
console.log(cherries)
// output
{
"name": "cherries",
"quantity": 5
}
Because we had a match, the "cherries" object was returned. If more than one matching element existed in the array, the first match is returned.
Given the arguments index and arr weren't needed for our simple example, this implementation can be refined further.
const cherries = inventory.find(item => item.name === "cherries")
console.log(cherries)
// output
{
"name": "cherries",
"quantity": 5
}
Because we have a function with a single parameter and a single, simple expression, the braces and return statement can be omitted entirely.
For the filter method, the implementation is the same. All that differs is what's returned. Instead of returning a matching object or undefined if no matches are found, we'll get back a shallow copy of the array we started with but only containing the matching elements.
Using the same example array, let's grab what inventory we're low on.
const lowInventory = inventory.filter(item => item.quantity <= 1)
console.log(lowInventory)
// output
[{
"name": "bananas",
"quantity": 0
}]
Easy enough. JavaScript has simple, elegant (dare I say Pythonic) solutions for these common problems.
Now let's see how we get the job done in Python.
Python
In the JavaScript example we began with find and moved on to filter. In Python, I'll reverse this and start with filter and end with a "find" equivalent (remember there's no direct equivalent built-in function in Python) because the "find" solution builds on filter.
The filter built-in function accepts two required, positional-only arguments: function and iterable. The function parameter is implemented similar to JavaScript where we need an expression that evaluates to True or False for each item in the iterable. Whereas the JavaScript array method was called with dot notation, here we have a function so we pass the iterable directly.
Let's use the same data from the JavaScript examples above but translate to a Python list.
inventory = [
{ 'name': 'apples', 'quantity': 2 },
{ 'name': 'bananas', 'quantity': 0 },
{ 'name': 'cherries', 'quantity': 5 },
]
Terminology
Whereas in JavaScript we had an array of objects, in Python we have a list of dictionaries.
In this translation, I made a couple of changes. First, we drop the declaration keyword const since Python has no such restriction on reassignment. Then, we need to wrap our dictionary keys in quotes, whereas JavaScript allows us to omit quotes for keys that are valid identifiers.
Now let's filter on our list of dictionaries against the same criteria used with the JavaScript example.
lowInventory = list(filter(lambda item: item['quantity'] <= 1, inventory))
print(lowInventory)
# output
[{'name': 'bananas', 'quantity': 0}]
Here we needed a couple additional tools to achieve the same result as our JavaScript example. First, I wrapped my filter expression in list(). This is because filter returns an iterator rather than a list—wrapping it in list() forces evaluation and gives us an actual list of results.
Next, I used lambda to pass an anonymous function to the filter built-in function. There's often a lot of confusion with lambda expressions and this isn't the discussion to get into the weeds, but lambdas are simply anonymous functions that accept parameters on the left of the colon, evaluate a single expression to the right, and return an object. I could have just as easily passed a named function and achieved the same result, just with more lines of code.
def isLow(item):
return item['quantity'] <= 1
lowInventory = list(filter(isLow, inventory))
print(lowInventory)
# output
[{'name': 'bananas', 'quantity': 0}]
Furthermore, we could achieve the same results as the filter expression but substituting it entirely for a generator expression.
lowInventory = list(item for item in inventory if item['quantity'] <= 1)
print(lowInventory)
# output
[{'name': 'bananas', 'quantity': 0}]
If we're being honest, this is your most concise, readable, and "Pythonic" solution. This is the implementation I would hope to encounter in production code for most scenarios. However, we're trying to compare apples to apples between JavaScript and Python and the filter built-in function is the most-direct comparison.
Now let's look at how we can translate the Python filter expression to the equivalent of JavaScript's find array method.
cherries = next(filter(lambda item: item['name'] == 'cherries', inventory))
print(cherries)
# output
{'name': 'cherries', 'quantity': 5}
It's that simple folks. All we needed to do is substitute list() for the next() built-in function. This function retrieves the next item from an iterator. However, we do need to be a little careful with this approach and the above example is a bit reckless.
oranges = next(filter(lambda item: item['name'] == 'oranges', inventory))
print(oranges)
# output
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
If the iterator is exhausted prior to finding an item, a StopIteration exception will be raised.
We can mitigate this in one of two ways.
First, we can simply provide a default value to the next function.
oranges = next(filter(lambda item: item['name'] == "oranges", inventory), None)
print(oranges)
# output
None
Or we could wrap the expression in a try/except block.
try:
oranges = next(filter(lambda item: item['name'] == 'oranges', inventory))
except StopIteration:
oranges = None
print(oranges)
# output
None
As with the previous example, we could substitute for a generator expression. But, this would only replace the contribution of the filter function so there's no need to provide a duplicate example.
Final Thoughts
What I hoped to demonstrate with this article (and potentially the articles to follow if I make this a series) is that what can be done in one language can often be very similarly done in another. That might seem obvious but it's foundational to developing competency in software engineering. It's far more efficient to master a single language then develop an understanding for where your core language and another differ than it is to memorize two discrete languages. Get good at one thing and map it onto another.
Here we looked at a couple common patterns between Python and JavaScript. Maybe for you it's Python and C++. Whatever the case, the same principles apply.