Подтвердить что ты не робот

Как "нарезать" коллекцию в Groovy

У меня есть набор объектов, которые я хочу разбить на коллекцию коллекций, где каждая последовательная группа из трех элементов находится в одной коллекции. Например, если у меня есть

def l = [1,4,2,4,5,9]

Я хочу превратить это в:

def r = [[1,4,2], [4,5,9]]

Я делаю это сейчас, повторяя сбор и разбивая его. Но мне тогда нужно передать эти "группы" в параллельную функцию, которая их обрабатывает. Было бы неплохо устранить этот O (n) предварительной обработки и просто сказать что-то вроде

l.slice(3).collectParallel { subC -> process(subC) }

Я нашел метод шагов в классе Range, но похоже, что он действует только на индексы. Любые умные идеи?

Обновление: Я не думаю, что это дубликат ссылочной ссылки, хотя он очень близок. Как было предложено ниже, это больше похоже на итераторную вещь, которую я ищу. Подкатегории затем будут переданы в GPars collectParallel. В идеале мне не нужно было бы выделять целую новую коллекцию.

4b9b3361

Ответ 1

Отъезд groovy 1.8.6. В списке есть новый метод сортировки.

def list = [1, 2, 3, 4]
assert list.collate(4) == [[1, 2, 3, 4]] // gets you everything   
assert list.collate(2) == [[1, 2], [3, 4]] //splits evenly
assert list.collate(3) == [[1, 2, 3], [4]] // won't split evenly, remainder in last list.

Взгляните на Groovy Список документации для получения дополнительной информации, потому что есть еще пара параметров, которые дают вам некоторые другие параметры, включая удаление остатка.

Что касается вашей параллельной обработки, вы можете совершить круиз по спискам с помощью gpars.

def list = [1, 2, 3, 4, 5]
GParsPool.withPool {
  list.collate(2).eachParallel {
     println it
  }
}

Ответ 2

Если вы правильно поняли, вы в настоящее время копируете элементы из первоначальной коллекции в подкатегории. Для получения дополнительных предложений по этим направлениям проверьте ответы на следующий вопрос: Разделить коллекцию на подкатегории в Groovy

Похоже, что то, что вы ищете, - это способ для подкатегорий эффективно представлять собой представление в оригинальной коллекции. В этом случае ознакомьтесь с методом List.subList(). Вы можете либо перебирать индексы от 0 до size() с шагом 3 (или любым выбранным размером среза), либо вы могли бы получить привязку и создать Iterable/List, который скрывал бы детали от вызывающего. Здесь реализация последней, вдохновленная тестом.

class Slicer implements Iterator {
  private List backingList
  private int sliceSize
  private int index

  Slicer(List backingList, int sliceSize) {
    this.backingList = backingList
    this.sliceSize = sliceSize
  }

  Object next() {
    if (!hasNext()) {
      throw new NoSuchElementException()
    }

    def ret
    if (index + sliceSize <= backingList.size()) {
      ret = backingList.subList(index, index+sliceSize)
    } else if (hasNext()) {
      ret = backingList.subList(index, backingList.size())
    }
    index += sliceSize
    return ret
  }

  boolean hasNext() {
    return index < backingList.size()
  }

  void remove() {
    throw new UnsupportedOperationException() //I'm lazy ;)
  }
}

Ответ 3

Мне нравятся оба решения, но вот немного улучшенная версия первого решения, которое мне очень нравится:

class Slicer implements Iterator {
private List backingList
private int sliceSize
private int index

Slicer(List backingList, int sliceSize) {
  this.backingList = backingList;
  int ss = sliceSize;

  // negitive sliceSize = -N means, split the list into N equal (or near equal) pieces  
  if( sliceSize < 0) {
      ss = -sliceSize;
      ss = (int)((backingList.size()+ss-1)/ss);
  }
  this.sliceSize = ss
}

Object next() {
  if (!hasNext()) {
    throw new NoSuchElementException()
  }

  def ret = backingList.subList(index, Math.min(index+sliceSize , backingList.size()) );
  index += sliceSize
  return ret
  }

  boolean hasNext() {
    return index < backingList.size() - 1
  }

  void remove() {
    throw new UnsupportedOperationException() //I'm lazy ;)
  }

  List asList() {
    this.collect { new ArrayList(it) }
  }

  List flatten() {
    backingList.asImmutable()
  }

}

// ======== TESTS

    def a = [1,2,3,4,5,6,7,8];
    assert  [1,2,3,4,5,6,7,8] == a;
    assert [[1, 2], [3, 4], [5, 6], [7, 8]] ==  new Slicer(a,2).asList(); 
    assert [[1,2,3], [4,5,6], [7,8]] == (new Slicer(a,3)).collect { it } // alternative to asList but inner items are subList
    assert [3, 2, 1, 6, 5, 4, 8, 7] == ((new Slicer(a,3)).collect { it.reverse() } ).flatten()

    // show flatten iterator
    //new Slicer(a,2).flattenEach { print it }
    //println ""

    // negetive slice into N pieces, in this example we split it into 2 pieces
    assert [[1, 2, 3, 4], [5, 6, 7, 8]] ==  new Slicer(a,-2).collect { it as List }  // same asList
    assert [[1, 2, 3], [4, 5, 6], [7, 8]] == new Slicer(a,-3).asList()
    //assert a == (new Slicer(a,3)).flattenCollect { it } 
    assert [9..10, 19..20, 29..30] == ( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) } )
    assert [[9, 10], [19, 20], [29, 30]] == ( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() } )

    println( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) } )
    println( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() } )

Ответ 4

Нет ничего встроенного, чтобы делать именно то, что вы хотите, но если мы @Delegate обращаются к итератору собственных списков, мы можем написать наш собственный класс, который работает точно так же, как итератор, который возвращает куски, которые вы ищете для:

class Slicer {
    protected Integer sliceSize 
    @Delegate Iterator iterator

    Slicer(objectWithIterator, Integer sliceSize) {
        this.iterator = objectWithIterator.iterator()
        this.sliceSize = sliceSize
    }

    Object next() {
        List currentSlice = []
        while(hasNext() && currentSlice.size() < sliceSize) {
            currentSlice << this.iterator.next()
        }
        return currentSlice
    }
}

assert [[1,4,2], [4,5,9]] == new Slicer([1,4,2,4,5,9], 3).collect { it }

Поскольку у него есть все методы, которые делает обычный Iterator, вы получаете бесплатные методы синтаксического сахара groovy с ленивой оценкой на все, что имеет метод iterator(), например диапазон:

assert [5,6] == new Slicer(1..100, 2).find { slice -> slice.first() == 5 }

assert [[9, 10], [19, 20], [29, 30]] == new Slicer(1..30, 2).findAll { slice -> !(slice[1] % 10) }