ruby 如何在JavaScript中声明Hash.new(0),默认值为0,用于计数对象?

bq8i3lrv  于 2023-11-18  发布在  Ruby
关注(0)|答案(3)|浏览(118)

我试着遍历一个数字数组,并计算每个数字在数组中被找到的次数。
在ruby中,这很简单,我只需要声明一个Hash.new(0),并且已经设置了从0开始计数的哈希值。例如:

arr = [1,0,0,0,1,0,0,1]
counter = Hash.new(0)
arr.each { |num| counter[num] += 1 } # which gives {1=> 3, 0=> 5}

字符串
我想在JavaScript中做同样的事情,但let counter = {}给了我{ '0': NaN, '1': NaN }
你知道如何在JavaScript中创建与Object相同的Hash吗?

bt1cpqcv

bt1cpqcv1#

ECMAScript不像Ruby对Hash es那样为对象中缺少的键设置默认值。然而,你可以使用 * 动态内省元编程 * 来做类似的事情,使用ECMAScript Proxy对象:

const defaultValue = 42;
const proxyHandler = {
    get: (target, name) => name in target ? target[name] : defaultValue
};
const underlyingObject = {};

const hash = new Proxy(underlyingObject, proxyHandler);

1 in hash
//=> false
1 in underlyingObject
//=> false

hash[1]
//=> 42
underlyingObject[1]
//=> undefined

字符串
所以,你可以这样做:

arr.reduce(
    (acc, el) => { acc[el]++; return acc }, 
    new Proxy(
        {},
        { get: (target, name) => name in target ? target[name] : 0 }
    )
)
//=> Proxy [ { '0': 5, '1': 3 }, { get: [Function: get] } ]


然而,这仍然不等同于Ruby版本,其中the keys of the Hash can be arbitrary objects,而ECMAScript对象中的属性键只能是Strings和Symbols。
Ruby Hash的直接等价物是ECMAScript Map
不幸的是,ECMAScript Map s也没有默认值。我们可以使用与对象相同的技巧来创建Proxy,但这会很尴尬,因为我们必须拦截对Mapget方法的访问,然后提取参数,调用has,等等。
幸运的是,Map被设计为可子类化:

class DefaultMap extends Map {
    #defaultValue;

    constructor(iterable=undefined, defaultValue=undefined) {
        super(iterable);
        this.#defaultValue = defaultValue;
    }

    get(key) {
        return this.has(key) ? super.get(key) : this.#defaultValue;
    }
}

const hash = new DefaultMap(undefined, 42);

hash.has(1)
//=> false

hash.get(1)
//=> 42


这允许我们做这样的事情:

arr.reduce(
    (acc, el) => acc.set(el, acc.get(el) + 1), 
    new DefaultMap(undefined, 0)
)
//=> DefaultMap [Map] { 1 => 3, 0 => 5 }


当然,一旦我们开始定义自己的Map,我们可能会一直这样做:

class Histogram extends DefaultMap {
    constructor(iterator=undefined) {
        super(undefined, 0);

        if (iterator) {
            for (const el of iterator) {
                this.set(el);
            }
        }
    }

    set(key) {
        super.set(key, this.get(key) + 1)
    }
}

new Histogram(arr)
//=> Histogram(2) [Map] { 1 => 3, 0 => 5 }


这也证明了一个非常重要的教训:数据结构的选择可以极大地影响算法的复杂性。通过正确选择数据结构(a Histogram),算法 * 完全消失 *,我们所做的就是示例化数据结构。
请注意,在Ruby中也是如此。By choosing the right data structure (there are several implementations of a MultiSet floating around the web), your entire algorithm vanishes and all that is left is

require 'multiset'

Multiset[*arr]
#=> #<Multiset:#5 0, #3 1>

t8e9dugd

t8e9dugd2#

在JavaScript中,你可以用Array.reduce来实现

const reducer = (acc, e) => acc.set(e, (acc.get(e) || 0) + 1);
[1, 0, 0, 0, 1, 0, 0, 1].reduce(reducer, new Map())
//⇒ Map(2) {1 => 3, 0 => 5}

字符串

kd3sttzy

kd3sttzy3#

你可以使用Map

  • hash初始化为Map
  • 遍历数组,如果key已经存在于hash中,则将其值增加1,否则将其设置为1
let arr = [1, 0, 0, 0, 1, 0, 0, 1]
let hash = new Map()

arr.forEach(val => {
  hash.set(val, (hash.get(val) || 0) + 1)
})

console.log([...hash])

字符串

相关问题