地图默认值
Map default value
我正在寻找类似地图默认值的内容。
m = new Map();
//m.setDefVal([]); -- how to write this line???
console.log(m[whatever]);
现在结果是 Undefined 但我想得到空数组 [].
首先回答关于标准 Map
的问题:ECMAScript 2015 中提出的 Javascript Map
不包括 setter 作为默认值。但是,这并不限制您自己实现该功能。
如果你只想打印一个列表,当 m[whatever] 未定义时,你可以:
console.log(m.get('whatever') || []);
正如 Li357 在他的评论中指出的那样。
如果你想重用这个功能,你也可以把它封装成一个函数,比如:
function getMapValue(map, key) {
return map.get(key) || [];
}
// And use it like:
const m = new Map();
console.log(getMapValue(m, 'whatever'));
但是,如果这不能满足您的需求,并且您真的想要一个具有默认值的地图,您可以为它编写自己的地图 class,例如:
class MapWithDefault extends Map {
get(key) {
if (!this.has(key)) {
this.set(key, this.default());
}
return super.get(key);
}
constructor(defaultFunction, entries) {
super(entries);
this.default = defaultFunction;
}
}
// And use it like:
const m = new MapWithDefault(() => []);
m.get('whatever').push('you');
m.get('whatever').push('want');
console.log(m.get('whatever')); // ['you', 'want']
截至 2022 年,Map.prototype.emplace
已达到 stage 2。
正如提案页面上所说,core-js 库中提供了一个 polyfill。
出于我的目的,我认为使用 DefaultMap class 扩展普通 Map 并添加其他方法会更清楚。这真的很好,因为它会导致更多的声明性代码。也就是说,当你声明一个新的Map时,不仅要声明键的类型和值的类型,还要声明默认值。
举个简单的例子:
// Using a primitive as a default value
const myMap1 = new DefaultMap<string, number>(123);
const myMap1Value = myMap1.getAndSetDefault("some_key");
// Using a factory function to generate a default value
const myMap2 = new DefaultMap<string, number, [foo: Foo]>((_key, foo) => foo.bar);
const foo = new Foo();
const myMap2Value = myMap2.getAndSetDefault("some_key", foo);
代码如下:
type FactoryFunction<K, V, A extends unknown[]> = (k: K, ...extraArgs: A) => V;
type FirstArg<K, V, A extends unknown[]> =
| Iterable<[K, V]>
| V
| FactoryFunction<K, V, A>;
type SecondArg<K, V, A extends unknown[]> = V | FactoryFunction<K, V, A>;
interface ParsedArgs<K, V, A extends unknown[]> {
iterable: Iterable<[K, V]> | undefined;
defaultValue: V | undefined;
defaultValueFactory: FactoryFunction<K, V, A> | undefined;
}
/**
* An extended Map with some new methods:
*
* - `getAndSetDefault` - If the key exists, this will return the same thing as the `get` method.
* Otherwise, it will set a default value to the key, and then return the default value.
* - `getDefaultValue` - Returns the default value to be used for a new key. (If a factory function
* was provided during instantiation, this will execute the factory function.)
* - `getConstructorArg` - Helper method for cloning the map. Returns either the default value or
* the reference to the factory function.
*
* When instantiating a new DefaultMap, you must specify either a default value or a function that
* returns a default value.
*
* Example:
* ```ts
* // Initializes a new empty DefaultMap with a default value of "foo"
* const defaultMapWithPrimitive = new DefaultMap<string, string>("foo");
*
* // Initializes a new empty DefaultMap with a default value of a new Map
* const defaultMapWithFactory = new DefaultMap<string, Map<string, string>>(() => {
* return new Map();
* })
*
* // Initializes a DefaultMap with some initial values and a default value of "bar"
* const defaultMapWithInitialValues = new DefaultMap<string, string>([
* ["a1", "a2"],
* ["b1", "b2"],
* ], "bar");
* ```
*
* If specified, the first argument of a factory function must always be equal to the key:
*
* ```ts
* const defaultMapWithConditionalDefaultValue = new DefaultMap<number, number>((key: number) => {
* return isOdd(key) ? 0 : 1;
* });
* ```
*
* You can also specify a factory function that takes a generic amount of arguments beyond the
* first:
*
* ```ts
* const factoryFunction = (_key: string, arg2: boolean) => arg2 ? 0 : 1;
* const defaultMapWithExtraArgs = new DefaultMap<string, string, [arg2: boolean]>(factoryFunction);
* ```
*/
export class DefaultMap<K, V, A extends unknown[] = []> extends Map<K, V> {
private defaultValue: V | undefined;
private defaultValueFactory: FactoryFunction<K, V, A> | undefined;
/**
* See the DefaultMap documentation:
* [insert link here]
*/
constructor(
iterableOrDefaultValueOrDefaultValueFactory: FirstArg<K, V, A>,
defaultValueOrDefaultValueFactory?: SecondArg<K, V, A>,
) {
const { iterable, defaultValue, defaultValueFactory } = parseArguments(
iterableOrDefaultValueOrDefaultValueFactory,
defaultValueOrDefaultValueFactory,
);
if (defaultValue === undefined && defaultValueFactory === undefined) {
error(
"A DefaultMap must be instantiated with either a default value or a function that returns a default value.",
);
}
if (iterable === undefined) {
super();
} else {
super(iterable);
}
this.defaultValue = defaultValue;
this.defaultValueFactory = defaultValueFactory;
}
/**
* If the key exists, this will return the same thing as the `get` method. Otherwise, it will set
* a default value to the key, and then return the default value.
*/
getAndSetDefault(key: K, ...extraArgs: A): V {
const value = this.get(key);
if (value !== undefined) {
return value;
}
const defaultValue = this.getDefaultValue(key, ...extraArgs);
this.set(key, defaultValue);
return defaultValue;
}
/**
* Returns the default value to be used for a new key. (If a factory function was provided during
* instantiation, this will execute the factory function.)
*/
getDefaultValue(key: K, ...extraArgs: A): V {
if (this.defaultValue !== undefined) {
return this.defaultValue;
}
if (this.defaultValueFactory !== undefined) {
return this.defaultValueFactory(key, ...extraArgs);
}
return error("A DefaultMap was incorrectly instantiated.");
}
/**
* Helper method for cloning the map. Returns either the default value or a reference to the
* factory function.
*/
getConstructorArg(): V | FactoryFunction<K, V, A> {
if (this.defaultValue !== undefined) {
return this.defaultValue;
}
if (this.defaultValueFactory !== undefined) {
return this.defaultValueFactory;
}
return error("A DefaultMap was incorrectly instantiated.");
}
}
function parseArguments<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
secondArg?: SecondArg<K, V, A>,
): ParsedArgs<K, V, A> {
return secondArg === undefined
? parseArgumentsOne(firstArg)
: parseArgumentsTwo(firstArg, secondArg);
}
function parseArgumentsOne<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
): ParsedArgs<K, V, A> {
const arg = firstArg as SecondArg<K, V, A>;
const { defaultValue, defaultValueFactory } =
parseDefaultValueOrDefaultValueFactory(arg);
return {
iterable: undefined,
defaultValue,
defaultValueFactory,
};
}
function parseArgumentsTwo<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
secondArg: SecondArg<K, V, A>,
): ParsedArgs<K, V, A> {
const firstArgType = type(firstArg);
if (firstArgType !== "table") {
error(
"A DefaultMap constructor with two arguments must have the first argument be the initializer list.",
);
}
const { defaultValue, defaultValueFactory } =
parseDefaultValueOrDefaultValueFactory(secondArg);
return {
iterable: firstArg as Iterable<[K, V]>,
defaultValue,
defaultValueFactory,
};
}
function parseDefaultValueOrDefaultValueFactory<K, V, A extends unknown[]>(
arg: SecondArg<K, V, A>,
): {
defaultValue: V | undefined;
defaultValueFactory: FactoryFunction<K, V, A> | undefined;
} {
if (typeof arg === "function") {
return {
defaultValue: undefined,
defaultValueFactory: arg as FactoryFunction<K, V, A>,
};
}
if (
typeof arg === "boolean" ||
typeof arg === "number" ||
typeof arg === "string"
) {
return {
defaultValue: arg as V,
defaultValueFactory: undefined,
};
}
return error(
`A DefaultMap was instantiated with an unknown type of: ${typeof arg}`,
);
}
我正在寻找类似地图默认值的内容。
m = new Map();
//m.setDefVal([]); -- how to write this line???
console.log(m[whatever]);
现在结果是 Undefined 但我想得到空数组 [].
首先回答关于标准 Map
的问题:ECMAScript 2015 中提出的 Javascript Map
不包括 setter 作为默认值。但是,这并不限制您自己实现该功能。
如果你只想打印一个列表,当 m[whatever] 未定义时,你可以:
console.log(m.get('whatever') || []);
正如 Li357 在他的评论中指出的那样。
如果你想重用这个功能,你也可以把它封装成一个函数,比如:
function getMapValue(map, key) {
return map.get(key) || [];
}
// And use it like:
const m = new Map();
console.log(getMapValue(m, 'whatever'));
但是,如果这不能满足您的需求,并且您真的想要一个具有默认值的地图,您可以为它编写自己的地图 class,例如:
class MapWithDefault extends Map {
get(key) {
if (!this.has(key)) {
this.set(key, this.default());
}
return super.get(key);
}
constructor(defaultFunction, entries) {
super(entries);
this.default = defaultFunction;
}
}
// And use it like:
const m = new MapWithDefault(() => []);
m.get('whatever').push('you');
m.get('whatever').push('want');
console.log(m.get('whatever')); // ['you', 'want']
截至 2022 年,Map.prototype.emplace
已达到 stage 2。
正如提案页面上所说,core-js 库中提供了一个 polyfill。
出于我的目的,我认为使用 DefaultMap class 扩展普通 Map 并添加其他方法会更清楚。这真的很好,因为它会导致更多的声明性代码。也就是说,当你声明一个新的Map时,不仅要声明键的类型和值的类型,还要声明默认值。
举个简单的例子:
// Using a primitive as a default value
const myMap1 = new DefaultMap<string, number>(123);
const myMap1Value = myMap1.getAndSetDefault("some_key");
// Using a factory function to generate a default value
const myMap2 = new DefaultMap<string, number, [foo: Foo]>((_key, foo) => foo.bar);
const foo = new Foo();
const myMap2Value = myMap2.getAndSetDefault("some_key", foo);
代码如下:
type FactoryFunction<K, V, A extends unknown[]> = (k: K, ...extraArgs: A) => V;
type FirstArg<K, V, A extends unknown[]> =
| Iterable<[K, V]>
| V
| FactoryFunction<K, V, A>;
type SecondArg<K, V, A extends unknown[]> = V | FactoryFunction<K, V, A>;
interface ParsedArgs<K, V, A extends unknown[]> {
iterable: Iterable<[K, V]> | undefined;
defaultValue: V | undefined;
defaultValueFactory: FactoryFunction<K, V, A> | undefined;
}
/**
* An extended Map with some new methods:
*
* - `getAndSetDefault` - If the key exists, this will return the same thing as the `get` method.
* Otherwise, it will set a default value to the key, and then return the default value.
* - `getDefaultValue` - Returns the default value to be used for a new key. (If a factory function
* was provided during instantiation, this will execute the factory function.)
* - `getConstructorArg` - Helper method for cloning the map. Returns either the default value or
* the reference to the factory function.
*
* When instantiating a new DefaultMap, you must specify either a default value or a function that
* returns a default value.
*
* Example:
* ```ts
* // Initializes a new empty DefaultMap with a default value of "foo"
* const defaultMapWithPrimitive = new DefaultMap<string, string>("foo");
*
* // Initializes a new empty DefaultMap with a default value of a new Map
* const defaultMapWithFactory = new DefaultMap<string, Map<string, string>>(() => {
* return new Map();
* })
*
* // Initializes a DefaultMap with some initial values and a default value of "bar"
* const defaultMapWithInitialValues = new DefaultMap<string, string>([
* ["a1", "a2"],
* ["b1", "b2"],
* ], "bar");
* ```
*
* If specified, the first argument of a factory function must always be equal to the key:
*
* ```ts
* const defaultMapWithConditionalDefaultValue = new DefaultMap<number, number>((key: number) => {
* return isOdd(key) ? 0 : 1;
* });
* ```
*
* You can also specify a factory function that takes a generic amount of arguments beyond the
* first:
*
* ```ts
* const factoryFunction = (_key: string, arg2: boolean) => arg2 ? 0 : 1;
* const defaultMapWithExtraArgs = new DefaultMap<string, string, [arg2: boolean]>(factoryFunction);
* ```
*/
export class DefaultMap<K, V, A extends unknown[] = []> extends Map<K, V> {
private defaultValue: V | undefined;
private defaultValueFactory: FactoryFunction<K, V, A> | undefined;
/**
* See the DefaultMap documentation:
* [insert link here]
*/
constructor(
iterableOrDefaultValueOrDefaultValueFactory: FirstArg<K, V, A>,
defaultValueOrDefaultValueFactory?: SecondArg<K, V, A>,
) {
const { iterable, defaultValue, defaultValueFactory } = parseArguments(
iterableOrDefaultValueOrDefaultValueFactory,
defaultValueOrDefaultValueFactory,
);
if (defaultValue === undefined && defaultValueFactory === undefined) {
error(
"A DefaultMap must be instantiated with either a default value or a function that returns a default value.",
);
}
if (iterable === undefined) {
super();
} else {
super(iterable);
}
this.defaultValue = defaultValue;
this.defaultValueFactory = defaultValueFactory;
}
/**
* If the key exists, this will return the same thing as the `get` method. Otherwise, it will set
* a default value to the key, and then return the default value.
*/
getAndSetDefault(key: K, ...extraArgs: A): V {
const value = this.get(key);
if (value !== undefined) {
return value;
}
const defaultValue = this.getDefaultValue(key, ...extraArgs);
this.set(key, defaultValue);
return defaultValue;
}
/**
* Returns the default value to be used for a new key. (If a factory function was provided during
* instantiation, this will execute the factory function.)
*/
getDefaultValue(key: K, ...extraArgs: A): V {
if (this.defaultValue !== undefined) {
return this.defaultValue;
}
if (this.defaultValueFactory !== undefined) {
return this.defaultValueFactory(key, ...extraArgs);
}
return error("A DefaultMap was incorrectly instantiated.");
}
/**
* Helper method for cloning the map. Returns either the default value or a reference to the
* factory function.
*/
getConstructorArg(): V | FactoryFunction<K, V, A> {
if (this.defaultValue !== undefined) {
return this.defaultValue;
}
if (this.defaultValueFactory !== undefined) {
return this.defaultValueFactory;
}
return error("A DefaultMap was incorrectly instantiated.");
}
}
function parseArguments<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
secondArg?: SecondArg<K, V, A>,
): ParsedArgs<K, V, A> {
return secondArg === undefined
? parseArgumentsOne(firstArg)
: parseArgumentsTwo(firstArg, secondArg);
}
function parseArgumentsOne<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
): ParsedArgs<K, V, A> {
const arg = firstArg as SecondArg<K, V, A>;
const { defaultValue, defaultValueFactory } =
parseDefaultValueOrDefaultValueFactory(arg);
return {
iterable: undefined,
defaultValue,
defaultValueFactory,
};
}
function parseArgumentsTwo<K, V, A extends unknown[]>(
firstArg: FirstArg<K, V, A>,
secondArg: SecondArg<K, V, A>,
): ParsedArgs<K, V, A> {
const firstArgType = type(firstArg);
if (firstArgType !== "table") {
error(
"A DefaultMap constructor with two arguments must have the first argument be the initializer list.",
);
}
const { defaultValue, defaultValueFactory } =
parseDefaultValueOrDefaultValueFactory(secondArg);
return {
iterable: firstArg as Iterable<[K, V]>,
defaultValue,
defaultValueFactory,
};
}
function parseDefaultValueOrDefaultValueFactory<K, V, A extends unknown[]>(
arg: SecondArg<K, V, A>,
): {
defaultValue: V | undefined;
defaultValueFactory: FactoryFunction<K, V, A> | undefined;
} {
if (typeof arg === "function") {
return {
defaultValue: undefined,
defaultValueFactory: arg as FactoryFunction<K, V, A>,
};
}
if (
typeof arg === "boolean" ||
typeof arg === "number" ||
typeof arg === "string"
) {
return {
defaultValue: arg as V,
defaultValueFactory: undefined,
};
}
return error(
`A DefaultMap was instantiated with an unknown type of: ${typeof arg}`,
);
}