【问题标题】:Mix two lists in groovy according to a pattern in Groovy?根据 Groovy 中的模式混合 groovy 中的两个列表?
【发布时间】:2012-12-04 09:22:10
【问题描述】:

我有两个列表可以说:

def list1 = [a,b,c,d,e...]
def list2 = [1,2,3,4,5... ]

我希望它们以某种模式混合,以便最终列表如下所示: [a,b,c,d,1,2,e,f,g,h,3,4,i,j,k,l,5,6...]

基本上在list1 中的每个n 元素之后,我都会从list2 中获得m 元素。

编辑:如果一个列表中的元素用完,则剩余列表中的项目应该简单地添加到最终列表中。

EDIT2:两个列表都可以将对象作为元素。

我想找到解决这个问题的最有效方法。

【问题讨论】:

  • 当列表中的元素用完时你会怎么做?
  • 我不使用 groovy。如果我用 Python 告诉你方法会有帮助吗?
  • @tim_yates:我只是将剩余列表中的项目添加在一起
  • @skyler 我从未在 python 中工作过 :(
  • @AtharvaJohri 所以下面的答案不好?

标签: algorithm collections groovy


【解决方案1】:

这是在 Groovy 中执行此操作的一种方法:

所以我有一个方法mix,它使用整数键(所需元素的数量)和列表作为值:

List mix( Map<Integer,List> amounts ) {
  amounts.collect { k, v ->
    v.collate( k )
  }.transpose().flatten()
}

那么,给定:

// The letters a to z
def list1 = 'a'..'z'

// The numbers 1 to 10
def list2 = 1..10

// Call, and ask for 4 of list1 followed by 2 of list2
mix( [ 4:list1, 2:list2 ] )

返回:

[ 'a', 'b', 'c', 'd', 1, 2,
  'e', 'f', 'g', 'h', 3, 4,
  'i', 'j', 'k', 'l', 5, 6,
  'm', 'n', 'o', 'p', 7, 8,
  'q', 'r', 's', 't', 9, 10 ]

(格式化为在这里更好看)

如您所见,它首先用完了数字,当它用完时,列表结束。这是因为当一个列表的元素用完时转置停止。

编辑:

用迭代器找到了另一种方式(所以它很懒,不会占用比其他所需更多的内存):

class MixingIterator<T> implements Iterator<T> {
  private int idx = 0
  private List<Iterator> iter
  private List<Integer>  amts
  
  MixingIterator( List<List> lists, List<Integer> amounts ) {
    iter = lists*.iterator()
    int i = 0
    amts = amounts.collectMany { [ i++ ] * it }
    // OR FOR GROOVY 1.7.8
    // amts = amounts.collect { [ i++ ] * it }.flatten()
  }
  
  private void moveIdx() {
    idx = ++idx % amts.size()
  }
  
  @Override boolean hasNext() {
    iter*.hasNext().any()
  }
  
  @Override T next() {
    if( !hasNext() ) { throw new NoSuchElementException() }
    while( !iter[ amts[ idx ] ].hasNext() ) { moveIdx() }
    T ret = iter[ amts[ idx ] ].next()
    moveIdx()
    ret
  }
  
  @Override void remove() {
    throw new UnsupportedOperationException()
  }
}

你叫它:

def list1 = 'a'..'z'
def list2 = 1..10

def ret = new MixingIterator( [ list1, list2 ], [ 4, 2 ] ).collect()
// OR FOR GROOVY 1.7.8
// def ret = new MixingIterator( [ list1, list2 ], [ 4, 2 ] ).collect { it }

然后 ret 将等于:

['a', 'b', 'c', 'd', 1, 2,
 'e', 'f', 'g', 'h', 3, 4,
 'i', 'j', 'k', 'l', 5, 6,
 'm', 'n', 'o', 'p', 7, 8,
 'q', 'r', 's', 't', 9, 10,
 'u', 'v', 'w', 'x', 'y', 'z']

【讨论】:

  • 我也需要剩余的元素。此外,我最初的两个清单都很大!做collect() 会有效吗?
  • @AtharvaJohri 无论如何,你会从他们两个中列出一个庞大的列表,不是吗?我将尝试一个迭代器版本,但我希望你最终会从这个迭代器中收集所有值?
  • 所以这说明:没有方法签名:java.util.ArrayList.collectMany() 适用于参数类型:(com.ds.core.CategoryController$_MixingIterator_closure1) 值:[com.ds. core.CategoryController$_MixingIterator_closure1@4062d86f] 可能的解决方案:collectAll(groovy.lang.Closure), collect(groovy.lang.Closure), collect(groovy.lang.Closure) 我的两个初始列表都有对象..抱歉没有具体!
  • @AtharvaJohri 你用的是什么版本的 Groovy?
  • @AtharvaJohri 如果小于 1.8.1,则将 Iterator 的构造函数中的 collectMany 行更改为:amts = amounts.collect { [ i++ ] * it }.flatten()
【解决方案2】:

我有一个基于主要答案保留剩余元素的功能解决方案。

List mix( Map<Integer,List> amounts ) {
    def maxItems = amounts.collect { k, v -> v.size() }.max()
    amounts.collect { k, v ->
        def padding = maxItems - v.size()
        it.addAll((1..padding).collect { null })
        v.collate( k )
    }.transpose().flatten().grep { it }
}

我在一个简化版本中对其进行了测试,以混合三个列表,一次取一个项目。这个想法只是使用空对象添加填充以使所有对象的长度相同。

【讨论】:

    猜你喜欢
    • 2011-02-05
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 2017-03-09
    • 2013-05-18
    • 2020-08-12
    • 1970-01-01
    相关资源
    最近更新 更多