Is there any way to leverage Groovy's collect method in combination with another iterator function?

后端 未结 2 1030
無奈伤痛
無奈伤痛 2020-12-20 08:13

For example, the groovy File class has a nice iterator that will filter out just directories and not files:

void eachDir(Closure closure) 

相关标签:
2条回答
  • 2020-12-20 08:31

    I don't know of any "idiomatic" way of doing this, nice riddle! =D

    You can try passing the eachDir, or any similar function, to a function that will collect its iterations:

    def collectIterations(fn) {
        def col = []
        fn {
            col << it
        }
        col
    }
    

    And now you can use it as:

    def dir = new File('/path/to/some/dir')
    def subDirs = collectIterations(dir.&eachDir)
    
    def file = new File('/path/to/some/file')
    def lines = collectIterations(file.&eachLine) 
    

    (that last example is equivalent to file.readLines())

    And only for bonus points, you may define this function as a method in the Closure class:

    Closure.metaClass.collectIterations = {->
        def col = []
        delegate.call {
            col << it
        }
        col
    }
    
    def dir = new File('/path/to/some/dir')
    def subDirs = dir.&eachDir.collectIterations()
    
    def file = new File('/path/to/some/file')
    def lines = file.&eachLine.collectIterations()
    

    Update: On the other hand, you might also do:

    def col = []    
    someDir.eachDir col.&add
    

    Which I think is quite less convoluted, but it's not leveraging the collect method as you requested :)

    0 讨论(0)
  • 2020-12-20 08:52

    Not for the specific example that you're talking about. File.eachDir is sort of a weird implementation IMO. It would have been nice if they implemented iterator() on File so that you could use the normal iterator methods on them rather than the custom built ones that just execute a closure.

    The easiest way to get a clean one liner that does what you're looking for is to use listFiles instead combined with findAll:

    dir1.listFiles().findAll { it.directory }
    

    If you look at the implementation of eachDir, you'll see that it's doing this (and a whole lot more that you don't care about for this instance) under the covers.

    For many similar situations, inject is the method that you'd be looking for to have a starting value that you change as you iterate through a collection:

    def sum = [1, 2, 3, 4, 5].inject(0) { total, elem -> total + elem }
    assert 15 == sum
    
    0 讨论(0)
提交回复
热议问题