Concatenating strings with RxJava2 - rx-java2

I have a simple List of my POJO called Sport which has a name field .
What is the best way to concat all the names in the List?
Observable.just(member.getSports())
.map(sports -> {
StringBuilder builder = new StringBuilder();
for (Sport sport : sports) {
builder.append(sport.getName());
}
return builder.toString()
})
I believe there must be a better way ?

Willi Mentzel is right that you don't need Rx, but if you want to use it anyway in plain Java for readability, here's a cool way to leverage Rx2
// Get a list of sports names
Observable<String> names = Observable.fromIterable(member.getSports()).map(sport -> sport.getName())
// Concat list into a single string output separated by comma
Single<String> listOfNames = names.collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
One-liner in Java:
Observable.fromIterable(member.getSports()).map(sport -> sport.getName()).collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
One-liner in Kotlin with RxKotlin
member.getSports().map { it.name }.toObservable().collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
Produces:
volleyball, hockey, racquetball

You should always strive for code that is easy to read and therefore to maintain.
You don't need RxJava for that, just streams:
String joined = member.getSports().stream()
.map(sport -> sport.getName())
.collect(Collectors.joining(""));
Edit 1:
Since streams are not an option. I wouldn't use RxJava then anyways. Just use the code from inside your lambda.
StringBuilder builder = new StringBuilder();
for (Sport sport : sports) {
builder.append(sport.getName());
}
builder.toString()
Edit 2:
Or you build a more generalized version, in case you need a prefix and postfix as well:
// Inside Utils.java
public static <T> String joinToString(List<T> list, String separator, String prefix, String postfix, Function<T, String> func) {
StringBuilder builder = new StringBuilder();
builder.append(prefix);
if (!list.isEmpty()) {
for (T t : list.subList(0, list.size() - 1)) {
builder.append(func.apply(t));
builder.append(separator);
}
builder.append(list.get(list.size() - 1));
}
builder.append(postfix);
return builder.toString();
}
and use it like this:
Utils.joinToString(member.getSports(), "", "", "", Sport::getName));
This comes pretty close to Kotlin's joinToString extension function.

Related

rxjava2 - how to zip Maybe that can be empty?

I am attempting to make 3 web services calls (e.g.: getPhoneNumber, getFirstName, getLastName) and collect the answers into a common object Person. Any of the web services calls can return a Maybe.empty().
When attempting to zip the response together, rxjava2 skips over the zip operation and terminate normally (without having aggregated my answer).
For a simplified example, see below:
#Test
public void maybeZipEmptyTest() throws Exception {
Maybe<Integer> a = Maybe.just(1);
Maybe<Integer> b = Maybe.just(2);
Maybe<Integer> empty = Maybe.empty();
TestObserver<String> observer = Maybe.zip(a, b, empty, (x, y, e) -> {
String output = "test: a "+x+" b "+y+" empty "+e;
return output;
})
.doOnSuccess(output -> {
System.out.println(output);
})
.test();
observer.assertNoErrors();
}
How can we collect empty values within a zip operation instead of having the zip operation skipped/ignored? If this is the wrong pattern to solve this problem, how would you recommend solving this?
For most use cases, leveraging the defaultIfEmpty method is the right way to go.
For representing something which is ultimately optional (doesn't even use default), I used Java 8 Optional type to represent.
For example
#Test
public void maybeZipEmptyTest() throws Exception {
Maybe<Optional<Integer>> a = Maybe.just(Optional.of(1));
Maybe<Optional<Integer>> b = Maybe.just(Optional.of(2));
Maybe<Optional<Integer>> empty = Maybe.just(Optional.empty());
TestObserver<String> observer = Maybe.zip(a, b, empty, (x, y, e) -> {
String output = "test: a "+toStringOrEmpty(x)+" b "+toStringOrEmpty(y)+" empty "+toStringOrEmpty(e);
return output;
})
.doOnSuccess(output -> {
System.out.println(output);
})
.test();
observer.assertNoErrors();
}
private String toStringOrEmpty(Optional<Integer> value){
if(value.isPresent()){
return value.get().toString();
}
else {
return "";
}
}

scala-arm. Return type

I'm using scala-arm library to automatically release/close resources (for example InputStream).
But the problem is that code below returns ExtractableManagedResource[Int], not just Int as I want.
val result = for(responseStream <- managed(response.getResponseBodyAsStream)) yield {
val localResult: Int = 1
localResult
}
// result is of type ExtractableManagedResource[Int]
Is there any option to return Int and overcome wrapping result to ExtractableManagedResource?
EDIT: I know that I can just declre result variable as var and assign to it from inside the for-comprehension, but I want more scala-idiomatic way, i.e. without using var
this is easier to implement with the monadic approach by using the aquireAndGet feature
managed(response.getResponseBodyAsStream) acquireAndGet {
responseStream =>
val localResult: Int = 1
localResult
}
From the documentation:
result.opt.get
Refer to your own link, under the title "Monadic style", for further details.

Inline parsing of IObservable<byte>

I have an observable query that produces an IObservable<byte> from a stream that I want to parse inline. I want to be able to use different strategies depending on the data source to parse discrete messages from this sequence. Bear in mind I am still on the upward learning curve of RX. I have come up with a solution, but am unsure if there is a way to accomplish this using out-of-the-box operators.
First, I wrote the following extension method to IObservable:
public static IObservable<IList<T>> Parse<T>(
this IObservable<T> source,
Func<IObservable<T>, IObservable<IList<T>>> parsingFunction)
{
return parsingFunction(source);
}
This allows me to specify the message framing strategy in use by a particular data source. One data source might be delimited by one or more bytes while another might be delimited by both start and stop block patterns while another might use a length prefixing strategy. So here is an example of the Delimited strategy that I have defined:
public static class MessageParsingFunctions
{
public static Func<IObservable<T>, IObservable<IList<T>>> Delimited<T>(T[] delimiter)
{
if (delimiter == null) throw new ArgumentNullException("delimiter");
if (delimiter.Length < 1) throw new ArgumentException("delimiter must contain at least one element.");
Func<IObservable<T>, IObservable<IList<T>>> parser =
(source) =>
{
var shared = source.Publish().RefCount();
var windowOpen = shared.Buffer(delimiter.Length, 1)
.Where(buffer => buffer.SequenceEqual(delimiter))
.Publish()
.RefCount();
return shared.Buffer(windowOpen)
.Select(bytes =>
bytes
.Take(bytes.Count - delimiter.Length)
.ToList());
};
return parser;
}
}
So ultimately, as an example, I can use the code in the following fashion to parse discrete messages from the sequence any time the byte pattern for the string '<EOF>' is encountered in the sequence:
var messages = ...operators that surface an IObservable<byte>
.Parse(MessageParsingFunctions.Delimited(Encoding.ASCII.GetBytes("<EOF>")))
...further operators to package discrete messages along with additional metadata
Questions:
Is there a more straight-forward way to accomplish this using just out of the box operators?
If not, would it be preferable to just define the different parsing functions (i.e. ParseDelimited, ParseLengthPrefixed, etc.) as local extensions instead of having a more generic Parse extension method that accepts a parsing function?
Thanks in advance!
Take a look at Rxx Parsers. Here's a related lab. For example:
IObservable<byte> bytes = ...;
var parsed = bytes.ParseBinary(parser =>
from next in parser
let magicNumber = parser.String(Encoding.UTF8, 3).Where(value => value == "RXX")
let header = from headerLength in parser.Int32
from header in next.Exactly(headerLength)
from headerAsString in header.Aggregate(string.Empty, (s, b) => s + " " + b)
select headerAsString
let message = parser.String(Encoding.UTF8)
let entry = from length in parser.Int32
from data in next.Exactly(length)
from value in data.Aggregate(string.Empty, (s, b) => s + " " + b)
select value
let entries = from count in parser.Int32
from entries in entry.Exactly(count).ToList()
select entries
select from _ in magicNumber.Required("The file's magic number is invalid.")
from h in header.Required("The file's header is invalid.")
from m in message.Required("The file's message is invalid.")
from e in entries.Required("The file's data is invalid.")
select new
{
Header = h,
Message = m,
Entries = e.Aggregate(string.Empty, (acc, cur) => acc + cur + Environment.NewLine)
});

What is the alternative for String.format() in GWT?

While GWT is not emulate all java's core, what can be used as alternative for:
String.format("The answer is - %d", 42)?
What is the ellegant and efficient pattern to inject arguments to message in GWT?
One elegant solution is using SafeHtml templates. You can define multiple such templates in an interface like:
public interface MyTemplates extends SafeHtmlTemplates {
#Template("The answer is - {0}")
SafeHtml answer(int value);
#Template("...")
...
}
And then use them:
public static final MyTemplates TEMPLATES = GWT.create(MyTemplates.class);
...
Label label = new Label(TEMPLATES.answer(42));
While this is a little bit more work to set up, it has the enormous advantage that arguments are automatically HTML-escaped. For more info, see https://developers.google.com/web-toolkit/doc/latest/DevGuideSecuritySafeHtml
If you want to go one step further, and internationalize your messages, then see also https://developers.google.com/web-toolkit/doc/latest/DevGuideI18nMessages#SafeHtmlMessages
You can simply write your own format function instead of doing brain storm.
public static String format(final String format, final String... args,String delimiter) {
String[] split = format.split(delimiter);//in your case "%d" as delimeter
final StringBuffer buffer= new StringBuffer();
for (int i= 0; i< split.length - 1; i+= 1) {
buffer.append(split[i]);
buffer.append(args[i]);
}
buffer.append(split[split.length - 1]);
return buffer.toString();
}
Because most (as in 99.999%) message formats are static, known at compile-time, the way GWT approaches it is to parse them at compile-time.
You'll generally use a Messages subinterface for its ability to localize the message, but you'll sometimes rather need SafeHtmlTemplates.
In the 0.001% when template is not known at compile time you can use Javascript sprintf (see: http://www.diveintojavascript.com/projects/javascript-sprintf) as in:
public static native String format (String format, JsArrayMixed values) /*-{
return vsprintf(format, values);
}-*/;
You can write your own.
I wrote a version that just work with Strings(%s):
public static String format(final String format, final Object... args)
{
checkNotNull(format);
checkNotNull(args);
final String pattern = "%s";
int start = 0, last = 0, argsIndex = 0;
final StringBuilder result = new StringBuilder();
while ((start = format.indexOf(pattern, last)) != -1)
{
if (args.length <= argsIndex)
{
throw new IllegalArgumentException("There is more replace patterns than arguments!");
}
result.append(format.substring(last, start));
result.append(args[argsIndex++]);
last = start + pattern.length();
}
if (args.length > argsIndex)
{
throw new IllegalArgumentException("There is more arguments than replace patterns!");
}
result.append(format.substring(last));
return result.toString();
}
why not writing a method like:
String appendAnswer(int result) {
return "The answer is - " + Integer.toString(result);
}
is resolving your problem because you do nothing like formatting in your code.
if you ever face the problem like converting integer/byte to Hex String you should use:
Integer.toString(int, 16);
I don't know GWT much but I am working on a GWT project and I needed this. While trying some alternatives, I have found that this is working;
import java.text.MessageFormat;
MessageFormat.format("The answer is - {0}", 42);
I don't know if the project's developers added something special to make this work or it is working by default.

working with Query String in GWT

I have to created a dynamic URLcontaining the user id and email parameters, which will direct to sign up form in my GWT application. I want to set and get the parameters in the query string. I have referred tp http://code.google.com/p/gwt-examples/source/browse/trunk/System/src/com/gawkat/gwt/system/client/global/QueryString.java?r=1241 but here QueryStringData is inaccessible to my project.Please tell me how I can do it? Any alternative could also help me.
#Stein, but there is (a query parameter tokenizer in GWT): e.g. Window.Location.getParameter("debug") will return the string value of the parameter debug.
Don't think there's a simple tokenized query string parser in GWT. But you can get the raw query string by using:
String queryString = Window.Location.getQueryString();
Parse it any way you like. I use it like this to set debug flags etc.:
boolean debugMode = Window.Location.getQueryString().indexOf("debug=true") >= 0;
Note that changing values in the query part of the url (between the ? and the #) will reload the page. While changing the "hash part" of the url (anything after the #) will not reload the page. Which is why the com.google.gwt.user.client.History uses the hash part.
If you want really want to parse the history token (hash part) to encode parameters, here's the code for that:
private static Map<String, String> buildHashParameterMap() {
final String historyToken = History.getToken();
Map<String, String> paramMap = new HashMap<String, String>();
if (historyToken != null && historyToken.length() > 1) {
for (String kvPair : historyToken.split("&")) {
String[] kv = kvPair.split("=", 2);
if (kv.length > 1) {
paramMap.put(kv[0], URL.decodeQueryString(kv[1]));
} else {
paramMap.put(kv[0], "");
}
}
}
return paramMap;
}
There is in-built support for getting all of the parameters.
Simply call:
Map<String, List<String>> parameterMap = Window.Location.getParameterMap();