Thread overview
Find indexes of elements matching a given value in an array
Sep 09, 2012
Samuele Carcagno
Sep 10, 2012
bearophile
Sep 10, 2012
timotheecour
Sep 10, 2012
Samuele Carcagno
September 09, 2012
I would like to find the indexes of all the elements of an array matching a certain value. I could simply loop over all the elements to do the job, but I was hoping for some ready made function that also works across different types (ints, floats etc...).

I saw the countUntil function in std.algorithm, but that gives me only the index of the first match, while I would like to get an array with the indexes of all the matches. Is there any function for that purpose?

Thanks for any help!
September 10, 2012
Samuele Carcagno:

> I would like to find the indexes of all the elements of an array matching a certain value. I could simply loop over all the elements to do the job, but I was hoping for some ready made function that also works across different types (ints, floats etc...).

I have written you some code, but it's not tested, it works with just arrays, items inside the struct is const and this causes some troubles, etc.

Writing good library code requires a lot of work. Writing it quickly is dangerous.


import std.stdio;

struct FindAllIndexes(T) {
    const T[] items;
    T x;
    private size_t _front;

    this(in T[] items_, T x_) pure nothrow {
        this.items = items_;
        this.x = x_;
        for ( ; _front < items_.length; _front++)
            if (x_ == items[_front])
                break;
    }

    @property bool empty() const pure nothrow {
        return _front >= items.length;
    }

    @property size_t front() const pure nothrow {
        return _front;
    }

    void popFront() pure nothrow {
        _front++;
        for ( ; _front < items.length; _front++)
            if (x == items[_front])
                break;
    }
}

FindAllIndexes!T findAllIndexes(T)(in T[] items, T x) {
    return typeof(return)(items, x);
}

void main() {
    const data = [1, 5, 7, 9, 5, 10, 3, 5];
    writeln(data.findAllIndexes(5));
}


A slower higher level version:


import std.stdio, std.algorithm, std.range;

auto findAllIndexes(R, T)(in R items, T x) {
    return iota(size_t.max)
           .zip(items)
           .filter!(p => p[1] == x)()
           .map!q{a[0]}();
}

void main() {
    const data = [1, 5, 7, 9, 5, 10, 3, 5];
    writeln(data.findAllIndexes(5));
}

Bye,
bearophile
September 10, 2012
Here's a modification to:
1) hide the intermediate struct (as usual in std.algorithm, I forgot what this trick is called)
2) work with ranges, not just arrays (ie will work with iota, see unittest)
3) accept input without "in" attribute;
4) accept arbitrary predicate, not just "==x"

Please comment if I'm doing anything not casher.

5) In particular, is there a way to avoid the dummy argument in this(int ignore)?
6) for (3), I didn't use "in": I believe that is the usual behavior in std.algorithm, where the caller is responsible to pass in items.dup or items.save if he so wishes. Is that true?
7) I had to remove nothrow and pure, is that needed in the general range case? how to fix it if it is?

----
unittest{
   assert(iota(10).findIndexes!`a%3==1`==[1,4,7]);
}
auto findIndexes(alias pred,R)(R items)  if(isInputRange!R) {
	import std.functional:unaryFun;
	struct FindIndexes(alias pred2) {
		private size_t _front;
		this(int ignore=0) {//TODO:this() not allowed so... anything better?
			while(!items.empty){
				if(pred2(items.front)) break;
				_front++;
				items.popFront;
			}
		}
		@property bool empty() const {
			return items.empty;
		}
		@property size_t front() const {
			return _front;
		}
		void popFront() {
			_front++;
			items.popFront;
			while(!items.empty){
				if(pred2(items.front)) break;
				_front++;
				items.popFront;
			}
		}
	}
	return FindIndexes!(unaryFun!pred)();
}
----
September 10, 2012
Thank you guys, that's fantastic! I'll try out your functions.