Какие способы работы с Map в Dart вы знаете?

Ответ

В Dart я активно использую Map для различных сценариев. Вот основные подходы:

1. Создание и инициализация Map:

// Литерал Map
final Map<String, int> scores = {
  'Alice': 100,
  'Bob': 85,
  'Charlie': 92,
};

// Конструктор
final emptyMap = Map<String, dynamic>();
final fromConstructor = Map.from(scores); // Копия

// Для неизменяемых данных
const constantMap = const {
  'apiUrl': 'https://api.example.com',
  'timeout': 30,
};

2. Преобразование коллекций в Map:

// Из List в Map
final users = ['Alice', 'Bob', 'Charlie'];
final userMap = Map.fromIterable(
  users,
  key: (user) => user.toLowerCase(),
  value: (user) => User(name: user),
);
// {'alice': User('Alice'), 'bob': User('Bob'), ...}

// Из двух List (ключи и значения)
final keys = ['id', 'name', 'email'];
final values = [1, 'Alice', 'alice@example.com'];
final map = Map.fromIterables(keys, values);
// {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}

// Из List<MapEntry>
final entries = [
  MapEntry('createdAt', DateTime.now()),
  MapEntry('updatedAt', DateTime.now()),
];
final fromEntries = Map.fromEntries(entries);

3. Работа с MapEntry:

// Создание Map через entries
final config = Map.fromEntries([
  MapEntry('theme', 'dark'),
  MapEntry('notifications', true),
  MapEntry('language', 'ru'),
]);

// Итерация по entries
config.entries.forEach((entry) {
  print('${entry.key}: ${entry.value}');
});

// Фильтрация через entries
final filtered = Map.fromEntries(
  config.entries.where((entry) => entry.value != null),
);

4. Полезные методы Map:

final map = {'a': 1, 'b': 2, 'c': 3};

// Проверка и доступ
if (map.containsKey('a')) {
  final value = map['a'];
}

// Безопасное получение с значением по умолчанию
final value = map['d'] ?? 0;
final value2 = map.putIfAbsent('d', () => 0);

// Обновление значений
map.update('a', (v) => v + 1);
map.update('d', (v) => v + 1, ifAbsent: () => 1);

// Удаление
map.remove('b');
map.removeWhere((key, value) => value < 2);

// Преобразование
final doubled = map.map((key, value) => MapEntry(key, value * 2));

5. Кастомные Map для специфичных нужд:

// Case-insensitive Map
class CaseInsensitiveMap<K extends String, V> implements Map<K, V> {
  final Map<String, V> _map = {};

  @override
  V? operator [](Object? key) {
    return _map[(key as String).toLowerCase()];
  }

  @override
  void operator []=(K key, V value) {
    _map[key.toLowerCase()] = value;
  }

  // ... остальная реализация Map
}

// Использование
final ciMap = CaseInsensitiveMap<String, int>();
ciMap['Hello'] = 1;
print(ciMap['HELLO']); // 1

6. JSON-сериализация (частая задача в Flutter):

// Map ↔ JSON
final map = {'name': 'Alice', 'age': 30};
final jsonString = jsonEncode(map);
final decodedMap = jsonDecode(jsonString) as Map<String, dynamic>;

// Для сложных объектов с json_serializable
@JsonSerializable()
class User {
  final String name;
  final int age;

  User(this.name, this.age);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

7. LinkedHashMap (дефолтная реализация) vs SplayTreeMap vs HashMap:

// Сохраняет порядок вставки (по умолчанию)
final ordered = LinkedHashMap<String, int>();

// Автоматическая сортировка по ключам
final sorted = SplayTreeMap<String, int>();
sorted['z'] = 1;
sorted['a'] = 2;
print(sorted.keys); // (a, z)

// Не гарантирует порядок, но может быть быстрее
final hashMap = HashMap<String, int>();

В Flutter-разработке Map особенно полезны для конфигурации, кэширования данных, передачи параметров между экранами и работы с JSON API. Я часто использую Map.fromIterable для преобразования списков из API в более удобные для работы структуры.

Ответ 18+ 🔞

А, ну ты глянь, какие тут у нас карты разложены! Я в Dart с Map'ами как рыба в воде, потому что без них нихуя не работает. Вот смотри, как я их обычно юзаю, чтобы не облажаться.

1. Сначала создаём эту штуку, ёпта:

// Самый простой способ, прямо в лоб
final Map<String, int> scores = {
  'Alice': 100,
  'Bob': 85,
  'Charlie': 92,
};

// А если с пустого листа начинать — конструктор в помощь
final emptyMap = Map<String, dynamic>();
final fromConstructor = Map.from(scores); // Сделал копию, и волнение ебать — оригинал не тронул

// Для константных штук, которые ни хуя не должны меняться
const constantMap = const {
  'apiUrl': 'https://api.example.com',
  'timeout': 30,
};

2. Превращаем одни коллекции в другие, как фокусник, блядь:

// Берём List и — хуяк! — уже Map
final users = ['Alice', 'Bob', 'Charlie'];
final userMap = Map.fromIterable(
  users,
  key: (user) => user.toLowerCase(),
  value: (user) => User(name: user),
);
// Получится {'alice': User('Alice'), 'bob': User('Bob'), ...}

// А если у тебя два списка — ключи отдельно, значения отдельно? Тоже не проблема!
final keys = ['id', 'name', 'email'];
final values = [1, 'Alice', 'alice@example.com'];
final map = Map.fromIterables(keys, values);
// {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'} — красота!

// Ну или через MapEntry, если ты любишь всё контролировать
final entries = [
  MapEntry('createdAt', DateTime.now()),
  MapEntry('updatedAt', DateTime.now()),
];
final fromEntries = Map.fromEntries(entries);

3. MapEntry — это как кирпичик, из которого карту собираешь:

// Собираем конфиг по кирпичикам
final config = Map.fromEntries([
  MapEntry('theme', 'dark'),
  MapEntry('notifications', true),
  MapEntry('language', 'ru'),
]);

// Потом по ним же и итерируешься
config.entries.forEach((entry) {
  print('${entry.key}: ${entry.value}');
});

// Или отфильтровать можешь, если что-то не нравится
final filtered = Map.fromEntries(
  config.entries.where((entry) => entry.value != null),
);

4. А вот методы, без которых нихуя не сделаешь, запоминай:

final map = {'a': 1, 'b': 2, 'c': 3};

// Проверяем, есть ли ключ, а то вдруг nil прилетит
if (map.containsKey('a')) {
  final value = map['a']; // Теперь спокойно берём
}

// Безопасное получение, чтобы не вылететь в сраку
final value = map['d'] ?? 0; // Если нет — будет ноль
final value2 = map.putIfAbsent('d', () => 0); // Или создай, если нету

// Обновляем значения, как настоящие мужики
map.update('a', (v) => v + 1);
map.update('d', (v) => v + 1, ifAbsent: () => 1); // А если ключа нет — создаст

// Удаляем всё ненужное
map.remove('b');
map.removeWhere((key, value) => value < 2); // Удалит всё, где значение меньше двух

// И даже преобразовать можно
final doubled = map.map((key, value) => MapEntry(key, value * 2));

5. Иногда стандартная карта — как хуй в пальто, неудобно. Делаем свою:

// Например, карта, которой похуй на регистр букв
class CaseInsensitiveMap<K extends String, V> implements Map<K, V> {
  final Map<String, V> _map = {};

  @override
  V? operator [](Object? key) {
    return _map[(key as String).toLowerCase()];
  }

  @override
  void operator []=(K key, V value) {
    _map[key.toLowerCase()] = value;
  }

  // ... остальную реализацию Map пишешь тут, это долго, но понятно
}

// Используем
final ciMap = CaseInsensitiveMap<String, int>();
ciMap['Hello'] = 1;
print(ciMap['HELLO']); // 1 — работает, ебать!

6. JSON-сериализация — это вообще святое в Flutter, без неё никуда:

// Просто Map в JSON и обратно
final map = {'name': 'Alice', 'age': 30};
final jsonString = jsonEncode(map);
final decodedMap = jsonDecode(jsonString) as Map<String, dynamic>;

// А для сложных объектов юзаем json_serializable, чтобы не ебаться
@JsonSerializable()
class User {
  final String name;
  final int age;

  User(this.name, this.age);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

7. И напоследок, ёпта, разные типы Map, чтобы не путаться:

// LinkedHashMap — по умолчанию, порядок вставки помнит
final ordered = LinkedHashMap<String, int>();

// SplayTreeMap — умная, сама сортирует по ключам
final sorted = SplayTreeMap<String, int>();
sorted['z'] = 1;
sorted['a'] = 2;
print(sorted.keys); // (a, z) — видишь, алфавит!

// HashMap — порядок не гарантирует, зато может быть быстрее
final hashMap = HashMap<String, int>();

В общем, в Flutter без Map — как без рук. Конфиги, кэш, параметры между экранами гонять, JSON от API разбирать — везде они. Особенно Map.fromIterable люблю, когда список с бэкенда приходит, а надо его в удобную структуру превратить. Главное — не переусердствовать, а то получится пиздопроебибна, а не код.