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

  时间:2022-05-11 07:43:21  阅读量:22  评论数:0  作者:Commando

我试图遍历一个数字数组,并计算每个数字在数组中被找到的次数。

在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中做同样的事情,但是让counter={}给出{'0':NaN,'1':NaN}。

您知道如何在JavaScript中创建与对象相同哈希吗?

网友解决方案:

ECMAScript没有对象中缺少键的默认值,就像Ruby处理哈希值一样。但是,可以使用动态内省元编程来执行类似的操作,使用ECMAScript代理对象:

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版本,在Ruby版本中,哈希的键可以是任意对象,而ECMAScript对象中的属性键只能是字符串和符号。

Ruby哈希的直接等价物是ECMAScript映射。

不幸的是,ECMAScript映射也没有默认值。我们可以使用与对象相同的技巧并创建代理,但这会很尴尬,因为我们必须拦截对映射的get方法的访问,然后提取参数,调用havs等等。

幸运的是,映射被设计成子类:

class DefaultMap extends Map {
    constructor(iterable=undefined, defaultValue=undefined) {
        super(iterable);
        Object.defineProperty(this, "defaultValue", { value: 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 }

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

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 [Map] { 1 => 3, 0 => 5 }

这也说明了一个非常重要的教训:数据结构的选择会极大地影响算法的复杂性。只要正确选择数据结构(直方图),算法就完全消失了,我们所做的就是实例化数据结构。

注意,在Ruby中也是如此。通过选择正确的数据结构(在web上有多集的几种实现),整个算法就消失了,剩下的就是:

require 'multiset'

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

原文地址(source):原文链接