www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Getting equivalent elements in a range/array

reply Andrej M. <none no.com> writes:
I want to turn this:
auto arr = [1, 1, 2, 3, 4, 4];

into this:
auto arr2 = [[1, 1], [2], [3], [4, 4]];

I want an array of arrays of the same elements. Lazy or not, I don't care.

I thought I could get away with this inside some while loop:
auto equals = array(filter!"a == b"(arr));
arr = arr[equals.length-1..$];

Nope.

I need this for some buffered output, where the requirement is the elements of
the buffer all need to have the same properties so a function can output a
buffer of elements in one call instead of calling the function for each element
(the function call is expensive).
May 07 2011
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 05/07/2011 09:07 PM, Andrej M. wrote:
 I want to turn this:
 auto arr = [1, 1, 2, 3, 4, 4];

 into this:
 auto arr2 = [[1, 1], [2], [3], [4, 4]];

 I want an array of arrays of the same elements. Lazy or not, I don't care.

 I thought I could get away with this inside some while loop:
 auto equals = array(filter!"a == b"(arr));
 arr = arr[equals.length-1..$];

 Nope.

 I need this for some buffered output, where the requirement is the elements of
the buffer all need to have the same properties so a function can output a
buffer of elements in one call instead of calling the function for each element
(the function call is expensive).
This seems to work, but needs more work. :) import std.stdio; import std.array; struct EquivalentElements { int[] range; int[] front_; this(int[] range) { this.range = range; this.front_ = popEqualFront(this.range); } bool empty() { return front_.empty; } int[] front() { return front_; } void popFront() { this.front_ = popEqualFront(this.range); } private int[] popEqualFront(ref int[] range) { int[] front; if (!range.empty) { do { front ~= range[0]; range = range[1..$]; } while (!range.empty && (front[$-1] == range[0])); } return front; } } void main() { writeln(EquivalentElements([1, 1, 2, 3, 4, 4])); }
May 07 2011
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Fantastic work, thanks! I'll look into more detail tomorrow, but it
looks good so far. Just added a function helper and made the struct
typed:

import std.array;
import std.range;

struct EquivalentElements(T)
{
   T range;
   T front_;

   this(T range)
   {
       this.range = range;
       this.front_ = popEqualFront(this.range);
   }

   bool empty()
   {
       return front_.empty;
   }

   T front()
   {
       return front_;
   }

   void popFront()
   {
       this.front_ = popEqualFront(this.range);
   }

   private T popEqualFront(ref T range)
   {
       T front;

       if (!range.empty) {
           do {
               front ~= range[0];
               range = range[1..$];

           } while (!range.empty &&
                    (front[$-1] == range[0]));
       }

       return front;
   }
}

EquivalentElements!(Range) equivalentElements(Range)(Range r)
{
    return typeof(return)(r);
}

// test
void main()
{
    foreach (elem; equivalentElements([1, 1, 2, 3, 4, 4]))
    {
        writeln(elem);
    }
}
May 07 2011
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrej M.:

 I want to turn this:
 auto arr = [1, 1, 2, 3, 4, 4];
 
 into this:
 auto arr2 = [[1, 1], [2], [3], [4, 4]];
 
 I want an array of arrays of the same elements. Lazy or not, I don't care.
Currently if you use group like this: writeln(arr.group()); You get: [Tuple!(int,uint)(1, 2), Tuple!(int,uint)(2, 1), Tuple!(int,uint)(3, 1), Tuple!(int,uint)(4, 2)] Andrei has recently said he wants to modify group() to make it work more like python itertools.groupby(), so it will do what you want (the duplicated items too will be lazy, as in Python). Bye, bearophile
May 08 2011
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Thanks, group seems to work fine too.
May 08 2011