Flutter - Check if an index exists in List

You can convert the List to a Map and check the keys:

List<int> numbers = [1, 2, 3, 4, 5,];

//Check if index 7 is valid
if (numbers.asMap().containsKey(7)) {
  print('Exists');
} else {
  print('Doesn\'t exist');
}

//[EDIT] Check if item at index 4 exists and is 5
if (numbers.asMap()[4] == 5) {
  print("Index 4 = 5");
} else {
  print("Index 4 != 5");
}

I'm confused by the other provided answers. Judging from the question text, all these helper functions and conversions of the list to maps and such is extreme overkill. What's wrong with a simple if check?

var list = [1, 2, 3, 4, 5];
var index = 4;
var value = 5;

if (list.length > index && list[index] == value) {
  ...
}

Elegance is in simplicity, not in a certain code format. Converting the list to a map takes work and you duplicate your data, all for literally no reason. Code readability may often be more important than performance these days, but that's no reason to use a knowingly terrible practice for such a minimal cosmetic gain.

And even if the above approach really bugs you so much, you can always just wrap it in an extension method:

extension ListExtensions<T> on List<T> {
  bool containsAt(T value, int index) {
    assert(this != null);
    return index >= 0 && this.length > index && this[index] == value;
  }
}

// Usage

var list = [1, 2, 3, 4, 5];
var index = 4;
var value = 5;

if(list.containsAt(value, index)) {
  ...
}

EDIT: The array.indices field in Swift is a Range, and calling contains checks if a value is within that range. Because of the way Range works, checking if a value is within it is a constant-time operation, which is why it's so efficient. In fact, the following approaches in Swift perform more or less identically:

let array = [1, 2, 3, 4, 5]
let idx = 2

// array.indices approach
if array.indices.contains(idx) {
  ...
}

// Manual check approach
if idx >= 0 && idx < array.count {
  ...
}

Flutter doesn't have the Range type, so trying to perform code acrobatics to get the equivalent-looking code results in a horribly inefficient way to simply check if an index exists in a list. For example, here's the comparison between the list.asMap().contains(idx) approach in the selected answer and it's plain-code equivalent:

var list = [1, 2, 3, 4, 5];
var idx = 2;

// asMap approach
if (list.asMap().containsKey(idx)) {
  ...
}

// Manual conversion and check approach
Map<int, int> map = {};
for (var i = 0; i < list.length; i++) {
  map[i] = list[i];
} 
if (map.containsKey(idx)) {
  ...
}

As you can see, converting the list to a Map is a linear process, not a constant one, so if the list is a long one this could take quite a long time. Not just that, but you also end up creating a completely redundant Map object that contains all the elements of the list as well as the indices as its keys, so you've essentially doubled your memory footprint (or even tripled it considering maps store both key and value). Hopefully, you can see why this approach to checking if a list contains an index is worse in every regard to the "normal" way. (And in a comment he suggests calling asMap twice????)


The Range type in Swift isn't that hard to make in Dart, though, and with the extension method approach from above you could achieve identical syntax AND performance:

(range.dart)

class Range extends Iterable<int> {
  const Range(this.start, this.end) : assert(start <= end);
  const Range.fromLength(int length) : this(0, length - 1);

  final int start;
  final int end;

  int get length => end - start + 1;

  @override
  Iterator<int> get iterator => Iterable.generate(length, (i) => start + i).iterator;

  @override
  bool contains(Object? index) {
    if (index == null || index is! int) return false;
    return index >= start && index <= end;
  }

  @override
  String toString() => '[$start, $end]';
}

(list_extensions.dart)

import 'range.dart';

extension ListExtensions on List {
  Range get indices => Range.fromLength(this.length);
}

(main.dart)

import 'list_extensions.dart';

main() {
  final list = [1, 2, 3, 4, 5];
  print(list.indices);              // [0, 4]
  print(list.indices.contains(3));  // true
  print(list.indices.contains(5));  // false
  print(list.indices.contains(-1)); // false
}

Having said all of this, the second aspect of your question wouldn't be covered by this, and you'd still have to check the index itself for the value. (Note that you would have to do this in Swift, too)

if (list.indices.contains(index) && list[index] == value) {
  // `value` exists in the list at `index`
  ...
}

Tags:

Dart

Flutter