18

I'm looking for an Erlang library function that will return the index of a particular element in a list.

So, if

X = [10,30,50,70]
lists:index_of(30, X)

would return 1, etc., just like java.util.List's indexOf() method.

Does such a method exist in the Erlang standard lib? I tried looking in the lists module but no luck. Or should I write it myself?

2240
  • 1,368
  • 1
  • 6
  • 18
Justin
  • 4,203
  • 5
  • 26
  • 54

6 Answers6

22

You'll have to define it yourself, like this:

index_of(Item, List) -> index_of(Item, List, 1).

index_of(_, [], _)  -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).

Note however that accesing the Nth element of a list is O(N), so an algorithm that often accesses a list by index will be less efficient than one that iterates through it sequentially.

sepp2k
  • 341,501
  • 49
  • 643
  • 658
15

As others noted, there are more efficient ways to solve for this. But if you're looking for something quick, this worked for me:

string:str(List, [Element]).
William Luxion
  • 157
  • 2
  • 4
5

Other solutions (remark that these are base-index=1):

index_of(Value, List) ->
   Map = lists:zip(List, lists:seq(1, length(List))),
   case lists:keyfind(Value, 1, Map) of
      {Value, Index} -> Index;
      false -> notfound
   end.

index_of(Value, List) ->
   Map = lists:zip(List, lists:seq(1, length(List))),
   case dict:find(Value, dict:from_list(Map)) of
      {ok, Index} -> Index;
      error -> notfound
   end.

At some point, when the lists you pass to these functions get long enough, the overhead of constructing the additional list or dict becomes too expensive. If you can avoid doing the construction every time you want to search the list by keeping the list in that format outside of these functions, you eliminate most of the overhead.

Using a dictionary will hash the values in the list and help reduce the index lookup time to O(log N), so it's better to use that for large, singly-keyed lists.

In general, it's up to you, the programmer, to organize your data into structures that suit how you're going to use them. My guess is that the absence of a built-in index_of is to encourage such consideration. If you're doing single-key lookups -- that's really what index_of() is -- use a dictionary. If you're doing multi-key lookups, use a list of tuples with lists:keyfind() et al. If your lists are inordinately large, a less simplistic solution is probably best.

Yan Foto
  • 8,951
  • 4
  • 45
  • 79
user502652
  • 59
  • 1
  • 1
1

This function is very uncommon for Erlang and this is may be reason why it is not in standard library. No one of experienced Erlang programmers need it and is discourage to use algorithms using this function. When someone needs it, can write for own purpose but this very rare occasions are not reason to include it to stdlib. Design your data structures in proper way instead of ask for this function. In most cases need of this function indicates error in design.

Hynek -Pichi- Vychodil
  • 25,511
  • 5
  • 49
  • 71
  • 1
    So what is your opinion - as an experienced Erlang programmer, I suppose - about the lists:nth and lists:nthtail functions? – Zed Sep 22 '09 at 19:16
  • 1
    That's just a ridiculous statement. You have absolutely no idea what I need this function for. – Justin Sep 23 '09 at 07:11
  • "No one of experienced Erlang programmers need it" Guess your "experienced Erlang programmers" don't tend to deal with real world problems then ? – Justin Sep 23 '09 at 07:13
  • @Justin: Well, what practical problem you are trying to solve? I wonder why Ericsson guys doesn't need this function in 1.7 million SLOC Erlang code in their code tend to include it to lists module. Well, enlighten me which for you need it. – Hynek -Pichi- Vychodil Sep 23 '09 at 07:36
  • I need to simulate N random variables, sort them, then find out which position in the sorted list the unsorted values lie. – Justin Sep 23 '09 at 08:26
  • 2
    In that case you could do Sorted = lists:sort(lists:zip(Randoms, lists:seq(1, length(Randoms)))), and then get the index of an item by lists:keyfind(Item, 1, Sorted). – Zed Sep 23 '09 at 08:34
  • Or you can even feed Sorted to a gb_tree if you access it frequently. – Zed Sep 23 '09 at 08:38
  • @Justin: Well, It means that mine response is exactly as I wrote. Solution is Just do it in different way and you doesn't need this function. Solution also performs better. – Hynek -Pichi- Vychodil Sep 23 '09 at 09:05
  • 2
    I guess it's just the way you say it. – Justin Sep 23 '09 at 09:09
-1

I think the writer makes a valid case. Here is my use case from a logging application. The objective is to check the severity of an error against the actions to be performed against various levels of error response.

get_index(A,L) ->
    get_index(A,L,1).
get_index(A,[A|_],N) ->
    N;
get_index(A,[_|T],N) ->
    get_index(A,T,N+1).

get_severity(A) ->
    Severity=[debug,info,warn,error],
    get_index(A,Severity).
tony wallace
  • 115
  • 6
  • I've just written a method very similar to this; I ended up using something like get_severity(debug) -> 1; get_severity(info) -> 2; etc - should be a bit faster, and dialyzer will notice if I pass it something invalid – Seb Feb 18 '14 at 15:17
  • Hey my solution is pretty much the same as the accepted solution, and it follows the erlang let it fail philosopy. I admit the accepted solution used better parameter names (as well as having not found return). Maybe the result should be {ok,Index), but I have been getting a little tired of having to extract what I want with okay({ok,X}) -> X. and then having dialyzer complain when I don't put the okay({error,X}) -> throw(X). – tony wallace Mar 28 '18 at 04:16
-1

The following function returns a list of indices of a given element in a list. Result can be used to get the index of the first or last occurrence of a duplicate element in a list.

indices_of(Element, L) ->                                                                                                                                                          
    Indices = lists:zip(lists:seq(1,length(L)), L),                                                                                                                                
    [ I || {I, E} <- Indices, E == Element ].   
savas
  • 1
  • 2