首页 > web前端 > js教程 > 正文

用 Javascript 计算东西

WBOY
发布: 2024-08-08 07:17:32
原创
662 人浏览过

Counting things in Javascript

如果您想计算数组中有多少特定品种的项目,您可以过滤该数组并检查结果的长度。

const letters = ['a','b','b','c','c','c'];
const numberOfC = letters.filter(letter => letter === 'c').length;
console.log(numberOfC); // 3
登录后复制

时间复杂度为 O(n),这是此类问题的理论理想值。它确实使用一些额外的内存来保存第二个数组,但对于仅计算一种项目,这是一种常见的方法,并且在大多数情况下足够高效。但是,如果您想要计算数组中每种项目的数量,那么这不是一种有效的方法。

过滤器的问题

如果我们不仅想要计算“c”的数量,还想要计算数组中每个字母的数量,那么这种技术的简单应用将是这样的:

const letters = ['a','b','b','c','c','c'];
letters.forEach((letter, _, arr) => {
   const count = arr.filter(otherLetter => otherLetter === letter).length;
   console.log(letter, count);
});
登录后复制

这会产生以下输出:

a 1
b 2
b 2
c 3
c 3
c 3
登录后复制

计数是正确的,但是存在冗余,可以通过首先使用 Set 来修复。

const letters = ['a','b','b','c','c','c'];
const uniqueLetters = new Set(letters);
for(const letter of uniqueLetters){
   const count = letters.filter(otherLetter => otherLetter === letter).length;
   console.log(letter, count);
};
登录后复制

结果输出没有重复项:

a 1
b 2
c 3
登录后复制

但是,它的时间复杂度确实为 O(n^2),因为每个过滤器都会迭代整个数组,并且它必须对每个唯一的字母执行此操作。

更有效的方法

可以以 O(n) 的时间复杂度执行此计数。一般原则是只迭代数组一次。对于遇到的每个项目,检查您是否已经有该项目的计数。如果您这样做,请将其增加 1。如果您不这样做,请为该项目添加一个计数,并将值设置为 1。

Javascript 有两种不同的机制适合存储计数。我们可以使用对象或地图。请注意,这是对 Map 数据结构的引用,而不是数组的 map() 方法。 使用 Map 效率更高,但使用对象来实现此目的仍然很常见,因此我们将了解如何使用两者进行计数。

用地图计数

const letters = ['a','b','b','c','c','c'];
const counts = new Map(); // Creates an empty Map
letters.forEach(letter => {
    if(counts.has(letter)) {//Determine if the letter already has a count
        counts.set(letter, counts.get(letter) + 1);//Increment existing count
    }
    else counts.set(letter, 1);//Set new count to 1
});
console.log(counts); //Map(3) { 'a' => 1, 'b' => 2, 'c' => 3 }
登录后复制

has() 检查该值是否已在 Map 中。 set() 在 Map 中存储一个值,要么创建一个新值,要么覆盖现有值。 get() 获取当前值。 请注意,has()、set() 和 get() 的时间复杂度为 O(1),因为内部使用了哈希。

如果我们想稍后提取特定字母的计数,我们可以这样做:

console.log(counts.get('c'));//3
登录后复制

用物体计数

虽然使用对象进行计数速度较慢,但​​如果您没有计算大量的项目,则性能差异可能不会明显。对象还比地图拥有更多的浏览器支持,尽管此时这种差异几乎变得无关紧要。据 caniuse.com 称,在撰写本文时,96.28% 的浏览器都支持 Map。对象是 Javascript 语言的一个组成部分,应该有 100% 的浏览器支持,但 caniuse.com 没有它的列表。 学习如何使用对象进行计数的主要原因是因为存在许多使用此技术的旧代码。一些从旧代码中学习的人也使用这种方法编写新代码。

const letters = ['a','b','b','c','c','c'];
const counts = {};//Create empty object for holding counts
letters.forEach(letter => {
    if(letter in counts) {//Check if the count exists
        counts[letter]++;//Increment the count
    }
    else counts[letter] = 1;//Set new count to 1
});
console.log(counts);//{ a: 1, b: 2, c: 3 }
登录后复制

此代码遵循与使用 Map 的代码相同的结构。差异在于映射和对象的语法。 “in”关键字用于测试对象是否具有具有该名称的键。括号语法用于获取和设置值。 请注意,测试成员资格、设置对象以及从对象获取值是 O(1) 时间复杂度操作,因为对象内部使用键的哈希值。

物体计数问题

如果您正在数的东西是物体,则需要格外小心。映射能够将对象存储为键,并且保留类型,但是如果您有一个完全不同的对象,并且具有完全相同的键和值,那么这会在相等比较方面产生问题。

const m = new Map();
m.set({name: "John"}, 5);
console.log(m.has({name: "John"}));//false
登录后复制

此代码输出“false”,因为两个对象不具有引用相等性。 这两个对象的键和值相同,但它们不是完全相同的对象。

const m = new Map();
const obj = {name: "John"}
m.set(obj, 5);
console.log(m.has(obj));//true
登录后复制

此版本的代码输出“true”,因为值集与正在测试成员资格的对象完全相同。

在大多数情况下尝试使用 Map 来计算对象的结果是所有对象的计数最终都会为 1,因为每个 has() 都会返回 false。

使用对象来计数对象有一个相关但略有不同的问题。对象键自动强制转换为字符串 (除非它们是符号)。如果您尝试使用一个对象作为另一个对象的键,则该类型强制会创建一个等于“[object Object]”的键。

const obj = {name: "John"}
const count = {};
count[obj] = 5;
console.log(count);// { '[object Object]': 5 }
登录后复制

The result of trying to count objects using objects is you will end up with an object that has "[object Object]" as the key, and the value will be the total number of all the things that were counted. There won't be any individual counts, because every item is treated as "[object Object]".

How to count objects

It is not common that you would need to count objects. If the need arises, the solution depends on whether or not the objects have a unique property that is guaranteed to be unique. For example, objects that have an id number could be counted by counting the id number occurrences instead of counting occurrences of the whole object.

const employees = [
    {id: 1, name: "John"},
    {id: 1, name: "John"},
    {id: 2, name: "Mary"},
    {id: 2, name: "Mary"},
    {id: 2, name: "Mary"},
]
const counts = new Map();
employees.forEach(employee => {
    if(counts.has(employee.id)) {
        counts.set(employee.id, counts.get(employee.id) + 1);
    }
    else counts.set(employee.id, 1);
});
console.log(counts);//Map(2) { 1 => 2, 2 => 3 }
登录后复制

This only gives counts with ids and an extra lookup would be needed to associate the counts with names, but we can fix that with a bit more code:

const countsWithNames = [];
for(const count of counts) {
    const employee = employees.find((employee) => employee.id === count[0]);
    countsWithNames.push({employee, count: count[1]});
}
console.log(countsWithNames);
登录后复制

The resulting output is:

[
  { employee: { id: 1, name: 'John' }, count: 2 },
  { employee: { id: 2, name: 'Mary' }, count: 3 }
]
登录后复制

If there isn't anything like an id that guarantees uniqueness, the only remaining practical option would be to first convert the object to JSON.

const people = [
    {name: "John Doe", age: 25},
    {name: "John Doe", age: 25},
    {name: "Mary Jane", age: 24},
    {name: "Mary Jane", age: 24},
    {name: "Mary Jane", age: 24}
]
const counts = new Map();
people.forEach(person => {
    const personString = JSON.stringify(person);
    if(counts.has(personString)) {
        counts.set(personString, counts.get(personString) + 1);
    }
    else counts.set(personString, 1);
});
console.log(counts);
登录后复制

The resulting output is:

Map(2) {
  '{"name":"John Doe","age":25}' => 2,
  '{"name":"Mary Jane","age":24}' => 3
}
登录后复制

Note that there are limitations to this approach. For example, objects with circular references cannot be stringified using JSON.stringify().

Counting non-array items

Both the techniques for counting with Maps and for counting with objects can be used to count anything, as long as you can find a way to iterate through the items. Some types of data can be converted to an array, for example, strings can be split, and keys of objects can be accessed as an array via Object.keys().

There are also some iterables that don't need to be converted into an array. For example, it's possible to iterate through the characters of a string by using an index. The same principle can be used in array-like structures like HTML Collections.

以上是用 Javascript 计算东西的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!