Skip to content

avoid_map_keys_contains

v0.4.0 Warning Fix Collection & Type

Flags map.keys.contains(key), which walks every key in order. map.containsKey(key) asks the hash table directly. The quick fix rewrites it.

Map.keys is an Iterable, so contains on it is a linear scan — O(n) per call, and this one runs once per row:

List<String> missingTranslations(
Map<String, String> translations,
List<String> requiredKeys,
) {
return requiredKeys
.where((key) => !translations.keys.contains(key))
.toList();
}
List<String> missingTranslations(
Map<String, String> translations,
List<String> requiredKeys,
) {
return requiredKeys.where((key) => !translations.containsKey(key)).toList();
}

The receiver does not have to be a plain variable — a field or any map-typed expression is matched just the same:

class Response {
const Response(this.headers);
final Map<String, String> headers;
// Don't
bool get isCached => headers.keys.contains('etag');
// Do
bool get isCachedFixed => headers.containsKey('etag');
}

Only .keys is checked. map.values.contains(x) genuinely has no hash-backed equivalent — containsValue is linear too — so it is never reported.

The receiver’s static type must resolve to a Map. A dynamic receiver, or one whose type the analyzer cannot infer, is left alone.

See also: Map.containsKey

This rule is in the recommended preset, so it is on with preset: recommended or preset: opinionated. Add it to preset: core with avoid_map_keys_contains: true.

To turn it off:

many_lints.yaml
rules:
avoid_map_keys_contains: false

To keep the rule on but skip certain paths, use per-rule exclude.