www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Find indexes of elements matching a given value in an array

reply "Samuele Carcagno" <sam.carcagno gmail.com> writes:
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!
Sep 09 2012
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
Sep 09 2012
next sibling parent "timotheecour" <thelastmammoth gmail.com> writes:
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)();
}
----
Sep 09 2012
prev sibling parent "Samuele Carcagno" <sam.carcagno gmail.com> writes:
Thank you guys, that's fantastic! I'll try out your functions.
Sep 10 2012