DaveAKing / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Make Splitter "generic" or able to support functional transforms pre-split. Barring this, add a Joiners/Splitters to allow creation of Splitters with custom Strategy objects. #1730

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Hello there; in the past I've had to deparse a String into numerous 
combinatorics of objects and collection types. Let us suppose I have a general 
method that requires splitting a comma-separated string into a variable 
collection type of a variable elementtype; 

I ultimately ended up with the following hack:

Splitter commaTrimmingSplitter = 
Splitter.on(",").trimResults().omitEmptyStrigs();

public static <E, T extends Collection<E>> splitAndParseString(String str, 
Function<String, E> parser, T returnCollection){
   checkNotNull(returnCollection);
   checkNotNull(str);
   FluentIterable.from(commaTrimmingSplitter.splitToList(str))
                 .transform(parser)
                 .filter(Predicates.notNull())
                 .copyInto(returnCollection);
   return returnCollection;
}

For something involving Maps, this becomes even more awkward to implement. 

Given that the Joiner class itself is final, and its Strategy is a private 
class, and given that there's no way to define a Splitter (or Joiner) with a 
parameterized Strategy, the same way you can create custom implementations of 
Multimap/Table with a parameterized Supplier, how hard would it to be to make 
Splitter<E> with DefaultSplitter implementing Splitter<String>, and adding 
(lazy pseudocode example):

public <T> Splitter.TransformingSplitter<T> withTransform(Function<String, T> 
transform){
   return new TransformingSplitter<T>(this, transform);
}

TransformingSplitter<T>{
    Splitter<String> originalSplitter
    Function<String, T> function;

    public <G> TransformingSplitter<T> withTransform(Function<T,G> transform){
        return new TransformingSplitter<G>(this, Functions.compose(transform, function);
    }

    //Implement the transformingIterator
}

Similar for MapSplitter, being able to transform element types in the map 
returned would be most helpful.

How feasible would it be for Splitter to be genericized so that by default it 
splits into a collection<String> or Map<String,String> but can in turn split 
into Collection<Integer>, Map<Integer,POJO>, etc?

How feasible would it also be to create a Splitter.splitInto(String unsplit, 
Collection<T> copyInto) void method? Similar to how Joiner has Joiner.appendTo, 
Splitter.splitInto would be helpful for assorted functions. 

Thanks again.
-Mike

Original issue reported on code.google.com by masal...@gmail.com on 16 Apr 2014 at 6:53

GoogleCodeExporter commented 9 years ago
I'm trying to understand why you see that code as a "hack". It seems the exact 
opposite of a hack to me.

Splitter's job is to be a splitter. It splits.

Original comment by kevinb@google.com on 16 Apr 2014 at 7:08

GoogleCodeExporter commented 9 years ago
A "hack" in that I am ultimately creating two collections: The String 
collection from the split, and then the actual element-type I wanted for my 
collection in the first place.

Splitter already can chain atop itself for omitting whitespace, empty elements, 
or null mapvalues. I don't see any real loss of cohesion in being able to 
decide what type of collection you're actually splitting *into*. 

Original comment by masal...@gmail.com on 16 Apr 2014 at 7:29

GoogleCodeExporter commented 9 years ago
If you use Splitter.split instead of splitToList, you're not actually creating 
two collections: the returned Iterable does the split lazily.  

There's no performance hit for splitting to an Iterable<String> and then 
transforming the Iterable with your parser.

Original comment by lowas...@google.com on 16 Apr 2014 at 7:33

GoogleCodeExporter commented 9 years ago
Ah, you mean:

385  public Iterable<String> split(final CharSequence sequence) {
386    checkNotNull(sequence);
387
388    return new Iterable<String>() {
389      @Override public Iterator<String> iterator() {
390        return splittingIterator(sequence);
391      }
392      @Override public String toString() {
393        return Joiner.on(", ")
394            .appendTo(new StringBuilder().append('['), this)
395            .append(']')
396            .toString();
397      }
398    };
399  }

Thanks for the clarification. I can make a few wrap-around classes to work off 
of this rule.

Original comment by masal...@gmail.com on 16 Apr 2014 at 7:40

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 16 Apr 2014 at 9:05

GoogleCodeExporter commented 9 years ago
I agree with decision not to extend Splitter API with transformation, however 
is there any reason why Splitter.split() doesn't return FluentIterable?

Original comment by tomas.za...@gmail.com on 17 Apr 2014 at 8:04

GoogleCodeExporter commented 9 years ago
Since Kevin's email response didn't get included in the issue: We are 
prohibited from having circular package dependencies.

Original comment by cgdecker@google.com on 17 Apr 2014 at 3:35

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:09

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:07