在JavaScript中重新实现流行的Python功能

My first language was Python. I love Python for its versatility, ease of usage, and wonderful community.

我的第一语言是Python。 我喜欢Python的多功能性,易用性和出色的社区。

Some months ago, I started venturing out deep into the JavaScript jungle as I had begun building out ViTeach (currently only available in German). ViTeach is a math platform which aims to fast-track and personalize the learning of mathematics with the use of machine learning and natural language processing. But this post is not about advertising my project. This post is about some basic Python functionalities that I missed while working with JavaScript. But not to fret, after an initial adjustment period to the language, things started to feel remarkably similar.

几个月前,当我开始构建ViTeach (目前仅以德语提供)时,我开始深入JavaScript丛林。 ViTeach是一个数学平台,旨在通过使用机器学习和自然语言处理来快速跟踪和个性化数学学习。 但是这篇文章与广告我的项目无关。 这篇文章是关于我在使用JavaScript时错过的一些基本Python功能。 但是不要担心,在对语言进行最初的调整之后,事情开始变得非常相似。

In this article, we will discuss three common patterns/functionalities that are not extremely obvious in JavaScript for someone coming from Python:

在本文中,我们将讨论三种常见的模式/功能,这些功能在JavaScript中对于来自Python的人不是很明显:

  • Range

    范围

  • List Comprehensions

    清单理解

  • Dictionary Comprehensions (and using the results to gain performance boosts for large array manipulations)

    字典理解 (并使用结果来提高大型数组操作的性能)

①范围 (① Range)

Image for post
Photo by Ashkan Forouzani on Unsplash
图片由 Ashkan ForouzaniUnsplash拍摄

The first big surprise to me was that JavaScript does not have an implementation of Python’s beloved range . The range function in Python returns a so-called sequence object, which is why we have to parse it into a list (or loop over it) to print out the values (e.g., in a Jupyter Notebook). But most of the time, you would anyway just iterate over the values so that step is rarely necessary. range is 0-indexed and takes:

给我的第一个大惊喜是JavaScript没有实现Python受欢迎的range 。 Python中的range函数返回一个所谓的sequence对象,这就是为什么我们必须将其解析为列表(或在其上循环)以打印出值的原因(例如,在Jupyter Notebook中)。 但是在大多数情况下,无论如何,您只会迭代这些值,因此很少需要执行此步骤。 range为0索引,取值:

  • one (max),

    一个(最大),
  • two (min and max),

    两个(最小和最大),
  • or three arguments (min, max, and step).

    或三个参数(最小,最大和步进)。

Be aware though, that the arguments for range are positional arguments (args) and not keyword arguments (kwargs).

但是请注意, range的参数是位置参数 (args),而不是关键字参数 (kwargs)。

Python (Python)

IN: (One Argument - only max value)list(range(10))OUT:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]IN: (Two Arguments - min and max value)list(range(3,11))OUT:[3, 4, 5, 6, 7, 8, 9, 10]IN: (Three Arguments - min, max and step value)list(range(7,34,3))OUT:[7, 10, 13, 16, 19, 22, 25, 28, 31]

JavaScript (JavaScript)

Jupp, there is no range function in JavaScript. The closest we can get for the first case (max only) is the following:

Jupp,JavaScript中没有range函数。 对于第一种情况,我们可以获得的最接近值(仅最大)如下:

IN:[...Array(5).keys()]OUT:[0,1,2,3,4]

Cmon, really?!? What is even happening here? Let’s dissect that. First, we instantiate an array with length 5 (Array(5)). At this point every value of that array is undefined. We then get the index (i.e., position within that array) of each element via .keys() and then ultimately destructure the list of indices into a new array via the destructuring ( ) operator and thereby creating a shallow copy. Wow, that certainly was a handful.

Cmon,真的吗?!? 这里到底发生了什么? 让我们剖析一下。 首先,我们实例化一个长度为5的数组( Array(5) )。 此时,该数组的每个值都是undefined 。 然后,我们通过.keys() )获得每个元素的索引(即,该数组中的位置.keys() ,然后最终通过destructuring ( )运算符将索引列表destructure为一个新数组,从而创建一个浅表副本。 哇,那肯定是少数。

Case two (min and max) and three (min, max, and step) require some more custom logic, which is why I decided to write my implementation and have used it in pretty much every project ever since.

情况2(最小和最大)和情况3(最小,最大和步进)需要更多的自定义逻辑,这就是为什么我决定编写实现并在此后的几乎每个项目中都使用它的原因。

const range = (min, max, steps = 1) => {// Step 1)
const arr = Array.from(
new Array(max - min),
(_, idx) => idx + min
);// Step 2)
return arr.filter((_, idx) => idx % steps === 0);
};IN:range(3,11)OUT:[3, 4, 5, 6, 7, 8, 9, 10]IN:range(7,34,3)OUT:[7, 10, 13, 16, 19, 22, 25, 28, 31]

Let’s quickly explain what this does:

让我们快速解释一下它的作用:

  1. Array.from() takes an array and a mapping function and applies the function to each element of an array-like or iterable object, think [func(idx,val) for (idx,val) in enumerate(arr)] in Python. We add the minimum to every value and now have a list of values from min to max. Note that instead of the new Array(max-min) we could have also used the function ([...Array(max-min).keys()]) we used earlier, but as I mentioned, I find the syntax a little bit strange.

    Array.from()接受一个数组和一个映射函数,并将该函数应用于类似数组或可迭代对象的每个元素,想想[func(idx,val) for (idx,val) in enumerate(arr)] Python [func(idx,val) for (idx,val) in enumerate(arr)][func(idx,val) for (idx,val) in enumerate(arr)] 。 我们将最小值添加到每个值,现在具有从最小值到最大值的值列表。 请注意,除了使用new Array(max-min)我们还可以使用之前使用的函数( [...Array(max-min).keys()] ),但是正如我提到的,我发现语法a有点奇怪。

  2. arr.filter() filters our array such that the resulting array only contains elements that meet the filter criteria. In our case the idx , i.e., position in the array modulo step has to be 0 (i.e., the number has to be divisible without remainder). Thus only taking every n-th element (where n = step)

    arr.filter()过滤我们的数组,使得结果数组仅包含满足过滤条件的元素。 在我们的情况下, idx ,即数组模阶中的位置必须为0(即,该数字必须是整除的,没有余数)。 因此,仅取第n个元素(其中n =步长)

②清单理解 (② List Comprehension)

Image for post
Photo by Nick Hillier on Unsplash
Nick HillierUnsplash拍摄的照片

List comprehensions, to me, are one of the main characteristics of Python and symbolic for a Pythonic way of coding. They allow getting rid of for-loops, which typically makes the code much more legible and concise. Let’s look at some common use-cases.

对我来说,列表理解是Python的主要特征之一,是Python编码方式的象征。 它们允许摆脱for循环,这通常使代码更清晰易懂。 让我们看一些常见的用例。

Python (Python)

A typical case is filtering elements from an array and then returning an array containing only the elements that meet a condition (e.g., type check).

典型的情况是从数组中过滤元素,然后返回仅包含满足条件的元素的数组(例如,类型检查)。

IN:
values = [1,2,3,5,"x",10]
[_ for _ in values if isinstance(_,int)]OUT:[1, 2, 3, 5, 10]

The other primary use case is the application of a function to every element in the list to return a new list with the updated values like this:

另一个主要用例是将函数应用于列表中的每个元素 ,以返回具有更新值的新列表,如下所示:

IN:
employees = [
    {
        "name": "John Don't",
        "performance": "medium",
        "salary": 10000
    },
    {
        "name": "Jane Does",
        "performance": "high",
        "salary": 9000
    }
]
def adjust_salary(employee):
    brackets = {
        "high": 1.2,
        "medium": 1.03,
        "low": 1
    }
    factor = brackets[employee["performance"]]
    current_salary = employee["salary"]
    return {
        "name" : employee["name"],
        "performance": employee["performance"],
        "salary": current_salary * factor
    }


[adjust_salary(employee) for employee in employees]


OUT:
[
   {
      "name":"John Don't",
      "performance":"medium",
      "salary":10300.0
   },
   {
      "name":"Jane Does",
      "performance":"high",
      "salary":10800.0
   }
]

Bonus: You could even go as far as to do nested list comprehensions like this:

奖励:您甚至可以做这样的嵌套列表理解:

IN:
matrix = [
[1, 2],
[3, 4],
[5, 6],
[7, 8]
]multiply_by_two = [
[cell * 2 for cell in row] for row in matrix
]OUT:[
[2 , 4 ],
[6 , 8 ],
[10, 12],
[14, 16]
]

JavaScript (JavaScript)

The pythonic list comprehension syntax did exist in JavaScript, although only in experimental versions. It was discussed for ECMAScript 4 but then dropped for good during ECMAScript 6. They might bring some form of general (i.e., works for all iterables) comprehension back in the future. But right now, arrow functions and map, filter and find will do the trick just fine. Let’s go through our previous examples. I am going to use _ as a way to label “throw-away variables” (since the underscore does not take up too much space). Let’s start with filtering:

尽管仅在实验版本中,但Python中确实存在pythonic list comprehension语法。 针对ECMAScript 4进行了讨论,但随后在ECMAScript 6中永久删除。它们可能会在将来带回某种形式的常规理解(即,适用于所有可迭代对象)。 但是现在,箭头功能和map, filter and find 会做的很好。 让我们来看一下前面的例子。 我将使用_作为标记“丢弃变量”的方法(因为下划线不会占用太多空间)。 让我们从过滤开始:

IN:
const values = [1,2,3,5,"x",10]
values.filter(_ => Number.isInteger(_))OUT:[1, 2, 3, 5, 10]

A special case of filter is find, which I found to be extremely useful. Find returns the first element that matches the condition.

filter一种特殊情况是find ,我发现它非常有用。 查找返回与条件匹配的第一个元素。

IN:
const values = [1,2,3,5,"x",10]
values.find(_ => _ > 4)OUT:5

Note: If no element meets the condition:

注意:如果没有元素满足条件:

  • find will return undefined (falsy value)

    find 将返回undefined ( 虚假 价值 )

  • whereas filter will return [] (truthy value —this is especially strange coming from Python, where an empty array is most certainly falsy — did I mention that JavaScript is weird sometimes?)

    filter将返回[] ( 真实值 -来自Python的情况尤其奇怪,Python中的空数组肯定是虚假的-我是否提到JavaScript有时很奇怪?)

Next up is the application of a function to every element in a list. Implementing this functionality can easily be achieved via means of map.

接下来是将函数应用到列表中的每个元素。 可以通过map轻松实现此功能。

IN:
const employees = [
    {
        "name": "John Don't",
        "performance": "medium",
        "salary": 10000
    },
    {
        "name": "Jane Does",
        "performance": "high",
        "salary": 9000
    }
]


const adjust_salary = (employee) => {
    const brackets = {
        "high": 1.2,
        "medium": 1.03,
        "low": 1
    }
    const factor = brackets[employee.performance]
    const current_salary = employee.salary
    return {
        "name" : employee.name,
        "performance": employee.performance,
        "salary": current_salary * factor
    }
}
    
employees.map(employee => adjust_salary(employee))


OUT:
[
   {
      "name":"John Don't",
      "performance":"medium",
      "salary":10300.0
   },
   {
      "name":"Jane Does",
      "performance":"high",
      "salary":10800.0
   }
]

③字典理解 (③ Dictionary Comprehension)

Image for post
Photo by Clay Banks on Unsplash
Clay BanksUnsplash拍摄的照片

Somewhat less common than list comprehensions are dictionary comprehensions, which I find immensely powerful to build mapping objects. Assume a scenario, where you have a lot of objects in a list, say all of them have unique ids and some other data like this:

字典理解比列表理解少一些常见,我发现它对构建映射对象非常有用。 假设存在一个场景,在该场景中列表中有很多对象,说它们全部具有唯一的ID和其他一些数据,如下所示:

{
'_id': '850f200c0b8f4b0ea80513e71fad93a0',
'name': 'qHvbtkRxrj',
'net_worth': 32000
}

Assume you got this list from a database and want to do some id based manipulation. You would then have to loop through the list countless times. A much easier way is to turn the list into a hash table, where the _id becomes the key and the object itself the value.

假设您从数据库中获得了此列表,并且想要进行一些基于id的操作。 然后,您将不得不无数次遍历列表。 一种更简单的方法是将列表转换为哈希表,其中_id成为键,而对象本身就是值。

hash_table = {
...,
'850f200c0b8f4b0ea80513e71fad93a0': {
'_id': '850f200c0b8f4b0ea80513e71fad93a0',
'name': 'qHvbtkRxrj',
'net_worth': 32000
},
...
}

Now, when we want to change the net worth of a particular entry, we could quickly look it up via its _id, change the object, and because the entries of the object are stored by reference, thereby altering the original list.

现在,当我们想更改特定条目的净资产时,我们可以通过其_id快速查找它,更改对象,并且因为该对象的条目是通过引用存储的,因此可以更改原始列表。

Python

Python

import uuid
import random
import string


# GENERATE SAMPLE DATA
sample_data = [{
    '_id': uuid.uuid4().hex,
     'name': ''.join(random.choices(
         [_ for _ in string.ascii_letters],
         k=10
     )),
    'net_worth': random.randint(10,100)*1000
} for i in range(1000)]




random_entry = random.choice(sample_data)
print(random_entry)


# BUILD THE HASH_TABLE
hash_table = {
    _['_id']:_
    for _ in sample_data
}


# CHANGE ENTRY VIA '_id'
hash_table[random_entry['_id']]['net_worth'] = 10000
print(random_entry)

Of course, you could also use the newly generated hash_table to retrieve the net_worth of a particular _id quickly. Using a hash table comes with a massive performance boost (10⁵) compared to looking the _ids up time and time again.

当然,您也可以使用新生成的hash_table快速检索特定_id的net_worth 。 与一次又一次地查看_ids相比,使用哈希表可显着提高性能(10⁵)

Image for post
Performance increase of factor 1⁰⁵ via using a hash_table 通过使用hash_table将性能提高1倍

JavaScript (JavaScript)

Dictionary comprehension is marginally trickier to pull off in JavaScript than in Python, but still fairly straightforward to implement. We are going to use reduce to achieve the desired outcome. Reduce takes an initial value and then iterates through all the values in the array and continuously updates the initial value.

在JavaScript中比在Python中理解字典理解要困难得多,但是实现起来仍然相当简单。 我们将使用reduce来实现所需的结果。 Reduce取一个初始值,然后遍历数组中的所有值并连续更新初始值。

const sample_data = [{'_id': 'cc7b7a9d8b9642299e3657af08dc0e2a',
  'name': 'zCKVZkcKvB',
  'net_worth': 45000},
 {'_id': '0ec97c94bc654b3a84f47591169f75eb',
  'name': 'LxfVwDeufE',
  'net_worth': 39000},
 {'_id': '1e2ea336022142c3be60ba174433c3d7',
  'name': 'ZjhiifKLCp',
  'net_worth': 68000},
 {'_id': '37ba69d4661744cdb4e6d2f3b66734ec',
  'name': 'WaVDrBSTxA',
  'net_worth': 37000},
 {'_id': 'aca980129610454597ae90296cc984ef',
  'name': 'QRsgCCLGEa',
  'net_worth': 83000},
 {'_id': 'addb82ec89244e869393708c1ab042bd',
  'name': 'fVtVcfatej',
  'net_worth': 33000},
 {'_id': '99ca22ac0df44c8e80a75ee242a4fa8e',
  'name': 'SGRRVqAyWf',
  'net_worth': 10000},
 {'_id': '832e3fbe3986424eb1a831d26a67e5d4',
  'name': 'dZZmNdQZlK',
  'net_worth': 93000},
 {'_id': '799191fd49034e3b8795bd463b69cadd',
  'name': 'URCMuxXsll',
  'net_worth': 51000},
 {'_id': '526d1a87190d4fcabee4e79e43616d69',
  'name': 'mUPevdfCEY',
  'net_worth': 19000}]


const result = sample_data.reduce((map, obj) => {
    map[obj._id] = obj;
    return map;
}, {});


console.log(result);

In this post, you learned how to get some of the tools back when venturing out into a new, seemingly scary world.

在这篇文章中,您学习了在进入一个新的,看起来令人恐惧的世界时如何找回一些工具。

翻译自: https://towardsdatascience.com/reimplementing-popular-python-functionalities-in-javascript-b3cfe8e7849f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值