34

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.

6
  • 8
    In dart var isIndexValid = array.asMap().containsKey(index) Commented Jan 16, 2020 at 5:37
  • What is the need of asMap() function? Commented Jan 16, 2020 at 5:38
  • Can't we just use .contains on the array instead of converting it to a map then finding the key? Commented Jan 16, 2020 at 5:39
  • @CrazyLazyCat Thanks. Exactly what I was looking for. Commented Jan 16, 2020 at 5:59
  • 1
    @YudhishthirSingh It's not the same. .contains checks if element exists in the array. And the question was how to check if element exists at a certain index or there is none. Commented Jan 16, 2020 at 6:07

5 Answers 5

43

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");
}
Sign up to request clarification or add additional context in comments.

7 Comments

The second part is overdoing. Just do if(numbers[4] == 5)
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.
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.
How about this condition if (numbers.asMap()[10] == null). It will not work in your case.
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))
|
34

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`
  ...
}

2 Comments

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 I've added an edit to my answer that addresses these questions.
2

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');
}

1 Comment

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.
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');

2 Comments

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 stackoverflow.com/a/35512668/11790692
@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.
-1

Easiest method is =>

List<int> numbers = [1, 2, 3, 4, 5,];    
if ($(numbers,6) != null) {
    print('Exists');
}

1 Comment

As far as I now $ is for string interpolation. The proposed solution yields an analyzer error ("The function '$' isn't defined").

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.