Thread overview
Reducing an array
Apr 18, 2014
Tim Holzschuh
Apr 18, 2014
monarch_dodra
Apr 18, 2014
bearophile
Apr 19, 2014
monarch_dodra
Apr 19, 2014
monarch_dodra
Re: *** GMX Spamverdacht *** Re: Reducing an array
Apr 19, 2014
Tim Holzschuh
Apr 19, 2014
MattCoder
Apr 19, 2014
Ali Çehreli
Apr 19, 2014
MattCoder
April 18, 2014
Hi there,

I try to remove all equal elements of an array, thus [2,2] --> [2].

I thought this maybe would be possible with std.algorithm.reduce, but at least the way I tried it doesn't work:

arr.reduce( (a,b) => a != b );

Is there a simple solution using Phobos-functions?

Thank you,
    Tim
April 18, 2014
On Thu, 17 Apr 2014 09:46:25 -0400, Tim Holzschuh via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:

> Hi there,
>
> I try to remove all equal elements of an array, thus [2,2] --> [2].
>
> I thought this maybe would be possible with std.algorithm.reduce, but at least the way I tried it doesn't work:
>
> arr.reduce( (a,b) => a != b );

reduce doesn't do what you think it does. It applies a function to all elements, keeping track of the result of each function call, and passing it to the next one.

In other words, reduce!fn(a, range) is like doing this:

fn(range[5], fn(range[4], fn(range[3], fn(range[2], fn(range[1], fn(range[0], a))))));

What you want is probably uniq:

http://dlang.org/library/std/algorithm/uniq.html

Note that it works on a SORTED range, so you want to sort first.

Note also that what it returns is not an array, but a lazily iterated range over the array. If you want another array, this is the code I would use:

arr.sort();
arr = arr.uniq.array();

-Steve
April 18, 2014
On Friday, 18 April 2014 at 20:27:20 UTC, Steven Schveighoffer wrote:
> Note also that what it returns is not an array, but a lazily iterated range over the array. If you want another array, this is the code I would use:
>
> arr.sort();
> arr = arr.uniq.array();
>
> -Steve

Out of curiosity, if the requirement was to *also* preserve ordering (eg: remove all non-first elements), how would you go at it?

[2, 1, 1, 3, 2, 3] => [2, 1, 3];

Maybe std.algorithm's `makeIndex` would help here?

Bonus points for doing it inplace.
April 18, 2014
monarch_dodra:

> Out of curiosity, if the requirement was to *also* preserve ordering (eg: remove all non-first elements), how would you go at it?
>
> [2, 1, 1, 3, 2, 3] => [2, 1, 3];
>
> Maybe std.algorithm's `makeIndex` would help here?
>
> Bonus points for doing it inplace.

This preserves ordering and it's in-place. Not tested much:

void main() {
    import std.stdio, std.traits;

    auto data = [2, 1, 1, 3, 2, 3];

    bool[ForeachType!(typeof(data))] seen;
    size_t pos = 0;
    foreach (immutable i; 0 .. data.length)
        if (data[i] !in seen) {
            if (pos != i)
                data[pos] = data[i];
            seen[data[i]] = true;
            pos++;
        }
    data.length = pos;

    data.writeln;
}


Bye,
bearophile
April 19, 2014
Am 18.04.2014 22:27, schrieb Steven Schveighoffer via Digitalmars-d-learn:
> arr.sort();
> arr = arr.uniq.array();
>
> -Steve
>
Thanks, also for the explanation!
- Tim
April 19, 2014
On Friday, 18 April 2014 at 22:11:17 UTC, bearophile wrote:
> monarch_dodra:
>
>> Out of curiosity, if the requirement was to *also* preserve ordering (eg: remove all non-first elements), how would you go at it?
>>
>> [2, 1, 1, 3, 2, 3] => [2, 1, 3];
>>
>> Maybe std.algorithm's `makeIndex` would help here?
>>
>> Bonus points for doing it inplace.
>
> This preserves ordering and it's in-place. Not tested much:
>
> void main() {
>     import std.stdio, std.traits;
>
>     auto data = [2, 1, 1, 3, 2, 3];
>
>     bool[ForeachType!(typeof(data))] seen;
>     size_t pos = 0;
>     foreach (immutable i; 0 .. data.length)
>         if (data[i] !in seen) {
>             if (pos != i)
>                 data[pos] = data[i];
>             seen[data[i]] = true;
>             pos++;
>         }
>     data.length = pos;
>
>     data.writeln;
> }
>
>
> Bye,
> bearophile

I thought of an approach somewhere along these lines. I was wondering if there was a UFCS approach too. Or an in-place approach.

Well, the "inplace" is easy of you accept N² performance :)
April 19, 2014
On Friday, 18 April 2014 at 20:02:41 UTC, Tim Holzschuh via Digitalmars-d-learn wrote:
> Hi there,
>
> I try to remove all equal elements of an array, thus [2,2] --> [2].
>
> I thought this maybe would be possible with std.algorithm.reduce, but at least the way I tried it doesn't work:
>
> arr.reduce( (a,b) => a != b );
>
> Is there a simple solution using Phobos-functions?

Not too fancy, since I'm new in D, but I created this:

import std.stdio;
import std.array;
import std.algorithm;

static if (!is(typeof(writeln)))
	alias writefln writeln;

void main(){
	int myfilter(int a){
		static int[] b;
		if(b.find(a) == []){
			b.insertInPlace(b.length, a);
			return a;
		}
		return -1;
	}
	
	auto arr = [1,1,2,3,2,2,4,5,5,1];	
	auto arrFiltered = arr.filter!(a => myfilter(a) > 0);
	
	writeln(arrFiltered);
}

Tested: http://dpaste.dzfl.pl/97a1307c7fec

I'm looking for creating something like C# "extensions", maybe with alias. I'll try later!

Matheus.
April 19, 2014
On 04/19/2014 09:55 AM, MattCoder wrote:

> On Friday, 18 April 2014 at 20:02:41 UTC, Tim Holzschuh via
> Digitalmars-d-learn wrote:
> void main(){
>      int myfilter(int a){
>          static int[] b;

That static variable makes this solution non-reentrant. To see an effect of this, replace the arrFiltered line with the following:

    import std.range;
    auto arr2 = [1,1,5,2,3];
    auto arrFiltered = zip(arr.filter!(a => myfilter(a) > 0),
                           arr2.filter!(a => myfilter(a) > 0));

Now the two filter operations get in each other's way and the output becomes:

    [Tuple!(int, int)(1, 5), Tuple!(int, int)(2, 3)]

I wonder what happened to 4. (?) :)

>          if(b.find(a) == []){

There is a more explicit way of saying that:

        if(!b.canFind(a)){

Ali

April 19, 2014
On Friday, 18 April 2014 at 22:11:17 UTC, bearophile wrote:
> This preserves ordering and it's in-place. Not tested much:
>
> void main() {
>     import std.stdio, std.traits;
>
>     auto data = [2, 1, 1, 3, 2, 3];
>
>     bool[ForeachType!(typeof(data))] seen;
>     size_t pos = 0;
>     foreach (immutable i; 0 .. data.length)
>         if (data[i] !in seen) {
>             if (pos != i)
>                 data[pos] = data[i];
>             seen[data[i]] = true;
>             pos++;
>         }
>     data.length = pos;
>
>     data.writeln;
> }
>
>
> Bye,
> bearophile

If you replace that "=" with a swap, then you can also preserve the duplicate elements at the end (although in no specific ordering):

import std.stdio : writefln;
import std.algorithm : canFind, swap;

//----
void main()
{
	auto arr = [1,1,5,2,3,2,2,4,5,5,1];
	size_t pos = 0;
	foreach(ref e; arr)
		if (!arr[0 .. pos].canFind(e))
			swap(arr[pos++], e);
	writefln("uniques: %s", arr[0 .. pos]);
	writefln("dupes:   %s", arr[pos .. $]);
}
//----

I was trying a "100% inplace" solution, but I found nothing better than N². It's  basically still what you submitted though.
April 19, 2014
On Saturday, 19 April 2014 at 17:12:10 UTC, Ali Çehreli wrote:
> On 04/19/2014 09:55 AM, MattCoder wrote:
>
> > On Friday, 18 April 2014 at 20:02:41 UTC, Tim Holzschuh via
> > Digitalmars-d-learn wrote:
> > void main(){
> >      int myfilter(int a){
> >          static int[] b;
>
> That static variable makes this solution non-reentrant...

Yes, you're completely right and I already knew that. But remember Like I said previously, I would like to convert that code to something close to "extensions" in C#, in that case I can take the address of the array to check if it's a new Filter or not. for example, current in D I can do this:

import std.stdio;
import std.array;
import std.range;
import std.algorithm;

void main(){
	int myfilter(int[] *addr, int a){
		static int[] b;
		static int[] *address;
		
		if(address != addr){
			address = addr;
			b.clear();
		}
		
		if(!b.canFind(a)){
			b.insertInPlace(b.length, a);
			return a;
		}
		return -1;
	}
	
	auto arr  = [1,1,2,3,2,2,4,5,5,1];	
	auto arr2 = [3,4,3,9,9,7,4,4,4,7];

	auto arrFiltered = arr.filter!(a => myfilter(&arr, a) >= 0);
	writeln(arrFiltered);
	
	auto arrFiltered2 = arr2.filter!(a => myfilter(&arr2, a) >= 0);
	writeln(arrFiltered2);	
}

But with extensions, the argument "address" (i.e: &arr) on the calling of myfilter can be avoided!

Matheus.