30

Let's say I have a list:

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

How can I check if object in the list exists at certain index??

E.g.

if (numbers[6] != null) {
  print('Exists');
}

I was hoping I can do smth like that, but apparently it's not working.

Shalugin
  • 1,092
  • 2
  • 10
  • 15

5 Answers5

39

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");
}
  • The second part is overdoing. Just do `if(numbers[4] == 5)` – Crazy Lazy Cat Jan 16 '20 at 05:37
  • 2
    It is, until you use an out of range index. In this example `(numbers[9] == 5)` will trigger an exception, but `(numbers.asMap()[9]==5)` will return false. – Carlos Javier Córdova Reyes Jan 16 '20 at 05:49
  • I like this one. The first part alone would be sufficient for my needs. But second part can be also useful for someone else. I mark it as accepted answer. – Shalugin Jan 16 '20 at 05:57
  • 1
    How about this condition `if (numbers.asMap()[10] == null)`. It will not work in your case. – mezoni Jan 16 '20 at 08:37
  • That's right. If you are checking for a null value you will need to use the first part too. Something like `if ((numbers.asMap().containsKey(10)) && (numbers.asMap()[10] == null))` – Carlos Javier Córdova Reyes Jan 16 '20 at 20:08
  • This approach is a ridiculous amount of overkill. Converting an entire `List` to a `Map` just to see if a specific value exists at a specific index? Operations like that are literally what lists are designed to do. – Abion47 Jan 17 '20 at 01:36
  • Doesnt work if List numbers = [0,1, 2, 3, 4, 5,6,7,8,9,10]; and you search for 0, then the key of the last item, 10 will be returned... – giorgio79 Apr 05 '22 at 04:24
30

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`
  ...
}
Abion47
  • 22,211
  • 4
  • 65
  • 88
  • You have a point. The reason why I like 'map' answer is that I come from Swift and in Swift to check an index you'd rather do ```array.indices.contains(index)``` than check an array length. So map answer with ```.containsKey(index)``` looks much more familiar to me. I agree that it does some extra job that is not really needed. Well... then my question is why can't you access indices of a list in Flutter as you can in Swift. Could it be that Swift does the same list to map conversion job behind the scenes?? – Shalugin Jan 18 '20 at 06:47
  • @Shalugin I've added an edit to my answer that addresses these questions. – Abion47 Jan 18 '20 at 17:29
1

Method 1

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

      //case 1 
      if(index<numbers.length && numbers[index] == find)
        print('$find exists at index $index');
      else
        print('$find not found at index $index');


      //case 2
      index = 7;

      if(index<numbers.length &&numbers[index] == find)
        print('$find exists at index $index');
      else
        print('$find not found at index $index');

Method 2 (EDIT # Added)

int index = 3, find = 4, foundAt;

  foundAt = numbers.indexOf(find);

  //case 1
  if (index == foundAt)
    print('Index of $find is $foundAt');
  else
    print('Value not found at $index');

  //case 2
  index = 7;
  if (index == foundAt)
    print('Index of $find is $foundAt');
  else
    print('Value not found at $index');
Naveen Avidi
  • 3,004
  • 1
  • 11
  • 23
  • I will vote it up, cuz it works. Though I was hoping to find a bit more elegant solution. Something like this one for Swift https://stackoverflow.com/a/35512668/11790692 – Shalugin Jan 16 '20 at 04:47
  • @Shalugin Who cares if it's inelegant if the "elegant" version is a horrendously bad practice? If the visible syntax bugs you so much, wrap it in a helper method so you never have to look at it. – Abion47 Jan 17 '20 at 01:37
1

There is a powerful native package for working with collections, in addition to solving this problem, you can find a lot of interesting things in it

import 'package:collection/collection.dart';

...

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

int element = numbers.firstWhereIndexedOrNull((i, _) => i == 6);

if (element != null) {
  print('Exists');
}
Alex
  • 1,457
  • 1
  • 13
  • 26
  • 1
    The collection package is awesome. I use `firstWhereOrNull` all the time as it cleans up much one off functions in my code like other suggestions. – Nick N Feb 14 '23 at 23:14
-1

Easiest method is =>

List<int> numbers = [1, 2, 3, 4, 5,];    
if ($(numbers,6) != null) {
    print('Exists');
}
Dhiroo Verma
  • 478
  • 1
  • 5
  • 13
  • As far as I now $ is for string interpolation. The proposed solution yields an analyzer error ("The function '$' isn't defined"). – mr_mmmmore Oct 26 '20 at 18:13