Restart observable based on its emitted values with RxJava - java

I have an observable which I want to emit Integer values, but if one of the emitted values is negative, I want the observable to emit values again with 1 second delay between emissions until there's no negative values.
I found this answer Repeat/Resubscribe to Observable with condition based on emitted item(s) and I don't really like it because it uses recursion which may slow a device down (depending on how many attempts it'd take to get the proper results).
Is there any other (proper) way of doing that? I also don't know how to set a delay to each emission except for the first one.
Here's what I have now:
getObservable().subscribe(valueList -> {
for (Integer value : valueList) {
Log.d("TAG", "value: " + value);
}
});
private Observable<List<Integer>> getObservable() {
return Observable.fromCallable(() -> {
List<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(12) - 1);
}
return list;
}).flatMap(valueList -> {
for (int i = 0; i < valueList.size(); i++) {
if (valueList.get(i) < 0) {
Log.d("TAG", "Negative value = " + valueList.get(i) + ", index = " + i);
return getObservable().delay(1, TimeUnit.SECONDS);;
}
}
return Observable.just(valueList);
});
}

Related

Java - How to implement a data structure that contains the number of clicks that were recorded on each domain AND each subdomain under it

I have a problem statement as described below:
Write a function that takes this input as a parameter and returns a data structure containing the number of
clicks that were recorded on each domain AND each subdomain under it.
For example, a click on "mail.yahoo.com" counts toward the totals for "mail.yahoo.com", "yahoo.com", and "com".
(Subdomains are added to the left of their parent domain. So "mail" and "mail.yahoo" are not valid domains.
Note that "mobile.sports" appears as a separate domain near the bottom of the input.)
Below is the input data:
String[] counts = {
"900,google.com",
"60,mail.yahoo.com",
"10,mobile.sports.yahoo.com",
"40,sports.yahoo.com",
"300,yahoo.com",
"10,stackoverflow.com",
"20,overflow.com",
"5,com.com",
"2,en.wikipedia.org",
"1,m.wikipedia.org",
"1,mobile.sports",
"1,google.co.uk"
};
Below is the expected output:
calculateClicksByDomain(counts) =>
com: 1345
google.com: 900
stackoverflow.com: 10
overflow.com: 20
yahoo.com: 410
mail.yahoo.com: 60
mobile.sports.yahoo.com: 10
sports.yahoo.com: 50
com.com: 5
org: 3
wikipedia.org: 3
en.wikipedia.org: 2
m.wikipedia.org: 1
mobile.sports: 1
sports: 1
uk: 1
co.uk: 1
google.co.uk: 1
I tried to write a solution for above problem statement:
Map<String, Integer> calculateClicksByDomainMap = new HashMap<>();
for(int i = 0; i < counts.length; i++) {
String[] seperateClickCountsAtComma = counts[i].split("\\,");
for(int j = 0; j < seperateClickCountsAtComma.length; j += 2) {
String clickCounts = seperateClickCountsAtComma[j];
String domain = seperateClickCountsAtComma[j+1];
calculateClicksByDomainMap.put(domain, Integer.parseInt(clickCounts));
}
}
for(Entry<String, Integer> domainCounts : calculateClicksByDomainMap.entrySet()) {
String domainName = domainCounts.getKey();
Integer domainCount = domainCounts.getValue();
splitStringOnOccurenceOfDot(domainName);
//System.out.println(domainName + " " + domainCount);
//String test[] = domainName.split("\\.");
//System.out.println(test[0] + "=======" + test[1] + "-----");
}
public static String splitStringOnOccurenceOfDot(String domainName) {
if(!domainName.contains(".")) {
return domainName;
}
String[] subdomain = domainName.split("\\.");
domainName = subdomain[1];
System.out.println(domainName + "===============" );
return splitStringOnOccurenceOfDot(domainName);
}
However, I'm not sure how to split the string using recursion.
Can anyone help me what is the efficient way to write code in order to get the expected output?
I there a way to solve it using recursion?
Thank you for your time.
The Map needs to ConcurrentHashMap, so that it allows concurrent updates.
First for loop with nesting can call the recursive method like below, might not need another for loop:
Map<String, Integer> calculateClicksByDomainMap = new ConcurrentHashMap<>();
for (final String count : counts) {
String[] separateClickCountsAtComma = count.split("\\,");
for (int j = 0; j < separateClickCountsAtComma.length; j += 2) {
Integer clickCount = Integer.parseInt(separateClickCountsAtComma[j]);
String domain = separateClickCountsAtComma[j + 1];
calculateClicksByDomainMap.put(domain, clickCount);
splitStringOnOccurenceOfDot(domain.substring(domain.indexOf(".")+1), clickCount, calculateClicksByDomainMap);
}
}
Rather than returning a value, recursion add/update map itself.
public static void splitStringOnOccurenceOfDot(String domainName, Integer domainCount, Map<String, Integer> calculateClicksByDomainMap) {
if(calculateClicksByDomainMap.containsKey(domainName)) {
Integer newCount = calculateClicksByDomainMap.get(domainName) + domainCount;
calculateClicksByDomainMap.put(domainName, newCount);
} else {
calculateClicksByDomainMap.put(domainName, domainCount);
}
if(domainName.contains(".")) {
splitStringOnOccurenceOfDot(domainName.substring(domainName.indexOf(".")+1), domainCount, calculateClicksByDomainMap);
}
}

How can I get last item of Flux without collapsing it with reduce() or last()

How can I get last item of Flux without collapsing it with reduce() or last() ? Here is my use-case:
1) I have generator that produces Flux<T> based on state.
2) When inner Flux completes it alters the state that affect next Flux objects I emit in generator.
Schematicaly it looks like this
static class State {
int secret = 2;
int iteration = 0;
}
Random rand = new Random(1024);
Flux<Integer> stream = Flux.<Flux<Integer>, State>generate(State::new, (state, sink) -> {
System.out.println(String.format("Generate: %d", state.secret));
Flux<Integer> inner = Flux.range(1, rand.nextInt(10));
sink.next(inner.doOnComplete(() -> {
// How do I get last item of `inner` here ?
// For example I'd like to decrement `state.secret` by last value of `inner`
}));
return state;
}).flatMap(Function.identity());
UPD: I unmarked my answer because the hack turned out to be unreliable. It's possible that .generate() will be invoked before previous Flux is fully consumed therefore making value in last incorrect.
First version was unreliable. I hacked another one:
static <T> Flux<T> expandOnLastItem(Supplier<Flux<T>> seed, Function<T, Flux<T>> generator) {
return Flux.just(new AtomicReference<T>())
.flatMap(last -> Flux.just(seed.get().materialize())
.flatMap(Function.identity())
.expand(v -> {
if (v.hasValue()) {
last.set(v.get());
} else if (v.isOnComplete() && last.get() != null) {
Flux<T> res = generator.apply(last.get());
last.set(null);
return res.materialize();
}
return Flux.empty();
})
.filter(s -> !s.isOnComplete())
.dematerialize());
}
which can be used as
static Flux<Integer> getPage(int pageId, int size) {
return Flux.defer(() -> {
if (pageId < 3) {
System.out.println("Returning data for pageId: " + pageId);
return Flux.range(pageId * 100, size);
} else {
System.out.println("Returning empty for pageId: " + pageId);
return Flux.empty();
}
});
}
expandOnLastItem(
() -> getPage(0, 5),
lastId -> {
System.out.println(" Expanding. Last item: " + lastId);
int curPage = lastId / 100;
return getPage(curPage + 1, 5);
})
.reduce(0L, (count, value) -> {
System.out.println("==> " + value);
return count + 1;
})
.block();
So I hacked it by mutating state variable in generator. It works but it's not very functional. If anyone else can suggest alternative I'll greatly appreciate it.
Random rand = new Random(1024);
Flux.<Flux<String>, State>generate(State::new, (state, sink) -> {
if (state.iteration < 4) {
final int count = rand.nextInt(10) + 1;
System.out.println(String.format("*** Generate %d: start %d (count %d)", state.iteration, state.secret, count));
Flux<Integer> inner = Flux.range(state.secret, count);
final int[] last = {Integer.MIN_VALUE};
sink.next(
inner
.doOnNext(value -> {
last[0] = value;
})
.map(value -> String.format("Iter %d value %d", state.iteration, value))
.doOnComplete(() -> {
System.out.println(String.format("Inner complete (last item was %d)", last[0]));
state.secret = last[0];
state.iteration += 1;
}));
} else {
System.out.println("Generate complete");
sink.complete();
}
return state;
})
.flatMap(Function.identity())
.map(value -> {
System.out.println(String.format("Ext map: %s", value));
return value;
})
.buffer(5)
.flatMapIterable(Function.identity())
.subscribe(value -> System.out.println(String.format(" ---> %s", value)));
System.out.println("Exiting");

JavaFx CheckTreeView how to check which item is unchecked?

I am using ControlsFX for CheckTreeView. I have lots of elements in CheckTreeView and i dont want to traverse through all the elements in this tree ( because it takes lots of time due to number of elements in the tree). Is there a method like checkTreeView.getLastUncheckedItem(); to get the last unchecked one.
Currently I am checking the number of elements that checked and comparing it with counter.
If (CountPrev > Count){
//Something Unchecked Do Stuff
}
But again, i cant find what is unchecked without traverse through all elements.
EDIT:
When user checks an item on CheckTreeView, I get that item by
String lastCheckedItem = checkTreeView.getCheckModel().
getCheckedItems().get(treeView.getCheckModel().getCheckedItems().size()-1).toString();
Now I need something like this for the unchecked item
Take a ArrayList 'allItem' and Store all TreeItems, then
after Store selected item in ObservableList 'Selecteditems' using
getCheckedItems() method, Now remove selected item in ArrayList like
below code:
Here allTreeItems is a CheckBoxTreeItem
List<String> allItem = new ArrayList<>();
for (int j = 0; j < allTreeItems.getValue().length(); j++) {
allItem.add(allTreeItems.getChildren().get(j).getValue());
}
if (CountPrev > Count) {
ObservableList<TreeItem<String>> Selecteditems = checkTreeView.getCheckModel().getCheckedItems();
allItem.remove(Selecteditems);
System.out.println("UnChecked Item :" + allItem);
for (int k = 0; k < allItem.size(); k++) {
System.out.println(allItem.get(k));
}
}
Guys thank you so much for your help!
I've accepted Calips' answer because of time and effort he gave for my question.
This is what I've been looking for:
checkTreeView.getCheckModel().getCheckedItems().addListener(new ListChangeListener<TreeItem<String>>() {
#Override public void onChanged(ListChangeListener.Change<? extends TreeItem<String>> change) {
updateText(checkedItemsLabel, change.getList());
while (change.next()) {
System.out.println("============================================");
System.out.println("Change: " + change);
System.out.println("Added sublist " + change.getAddedSubList());
System.out.println("Removed sublist " + change.getRemoved());
System.out.println("List " + change.getList());
System.out.println("Added " + change.wasAdded() + " Permutated " + change.wasPermutated() + " Removed " + change.wasRemoved() + " Replaced "
+ change.wasReplaced() + " Updated " + change.wasUpdated());
System.out.println("============================================");
}
}
});
Resource:
https://github.com/jinghai/controlsfx/blob/master/controlsfx-samples/src/main/java/org/controlsfx/samples/checked/HelloCheckTreeView.java
Stack<YourClassOfTheCheckModel> recentlyUnchcked = new Stack<YourClassOfTheCheckModel>();
yourTreeView.getSelectionModel().
selectedItemProperty().addListener( new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<YourClassOfTheCheckModel> selectedItem =
(TreeItem<YourClassOfTheCheckModel>) newValue;
CheckModel checkModel = checkTreeView.getCheckModel().
bool checked = checkModel.isChecked (selectedItem);
if(checked==false){
recentlyUnchcked.push(yourObjectOfTheCheckModel);
}
}
});
Hope this will help or give you an idea though i don't know if this will work (code not tested, have no IDE right now).

Addition of ArrayList values having same Date

I am having a sorted ArrayList like-
List<DD_Details> list = new ArrayList<DD_Details>();
list.add(new DD_Details(26/05/2014,3000.00));
list.add(new DD_Details(26/08/2014,6000.00));
list.add(new DD_Details(26/08/2014,2000.00));
DD_Details Class is -
class DD_Details {
private Date ddSubmissionDate;
private Double amount;
public DD_Details(Date n, Double s) {
this.ddSubmissionDate = n;
this.amount = s;
}
public Date getDdSubmissionDate() {
return ddSubmissionDate;
}
public void setDdSubmissionDate(Date ddSubmissionDate) {
this.ddSubmissionDate = ddSubmissionDate;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public String toString() {
return "ddSubmissionDate: " + this.ddSubmissionDate + "-- amount: "
+ this.amount;
}
}
i just want to add amount values of the same date and store in a new ArrayList.
I tried below code but it is not working properly.
for (int i = 0; i < list.size(); i++) {
Double amt = 0.0;
Date date1 = list.get(i).getDdSubmissionDate();
for (int j = i + 1; j < list.size(); j++) {
if (date1.equals(list.get(j).getDdSubmissionDate())) {
amt = amt + list.get(j).getAmount() + list.get(i).getAmount();
} else {
amt = list.get(i).getAmount();
}
}
list1.add(new DD_Details(date1, amt));
}
Please give me some hint to get it done. Thanks in Advance.
You can use next solution instead:
List<DD_Details> list = new ArrayList<DD_Details>();
List<DD_Details> list1 = new ArrayList<DD_Details>();
list.add(new DD_Details(new Date(2014, 5, 26), 3000.00));
list.add(new DD_Details(new Date(2014, 8, 26), 6000.00));
list.add(new DD_Details(new Date(2014, 8, 26), 2000.00));
for (DD_Details currentEl : list) // iterate over 'list'
{
DD_Details existingElement = null;
for (DD_Details el1 : list1) // find element in 'list1' with the same date
{
if (el1.getDdSubmissionDate().equals(currentEl.getDdSubmissionDate()))
{
existingElement = el1;
break;
}
}
if (existingElement == null) // if element is not found in 'list1' then add current element to list
{
list1.add(currentEl); // or list1.add(new DD_Details(el.getDdSubmissionDate(), el.getAmount()))
}
else // if element is found, then increase amount
{
existingElement.setAmount(existingElement.getAmount() + currentEl.getAmount());
}
}
// 'list1' contains what you need
In the inner for, when you are adding the values for a particular date, there is an error in the else part. You try to iterate through the list for a given date and add all the values until the date is different. Once you reach this condition, you get inside the else and set the sum to be the amount of the first element of this given date, therefore you are overriding the sum you had calculated. Moreover, for every new element with the same date, you are adding the amount of the first date, which means that if there are 4 elements of the same date, you will be adding 3 times the first amount.
What you should do, is get the amount for that given date before entering the second loop. One final consideration, is that you are going to get different amounts for different dates because imagine you have 3 elements with the same date, with the loop you are using, you will start with the first one, get the amount of the 3 elements added, then go to the second and get the amount of the second and third added, and finally move to the last one and create a third element with the amount of only the third element. Therefore, you should create a third variable which I called k, and store the last j value that contained the same date, to then add it to i and avoid iterating through an element with a date you already processed:
for (int i = 0; i < list.size(); i++) {
Double amt = list.get(i).getAmount();
Date date1 = list.get(i).getDdSubmissionDate();
int k = 0;
for (int j = i + 1; j < list.size(); j++) {
if (date1.equals(list.get(j).getDdSubmissionDate())) {
amt = amt + list.get(j).getAmount();
k = j;
}
}
list1.add(new DD_Details(date1, amt));
i += k++;
}
Your dates should be String literals. The way you've written them they will be ints and all equal to 0.
The obvious solution would be to build a Map of dates to amounts (instead of a list) and after adding all the entries iterating the map to build your list. Something like:
Map<Date, Double> amountPerDate = new HashMap<>();
List<Date> dates = ...;
List<Double> amounts = ...; // wherever those may come from
for(int i = 0; i < dates.size(); i++) {
Double currentAmount = amountPerDate.get(dates.get(i));
double amount = currentAmount == null ? 0 : currentAmount;
amountPerDate.put(dates.get(i), amount + amounts.get(i));
}
List<DD_Details> details = new ArrayList<>();
for(Entry<Date, Double> e : amountPerDate) {
details.put(new DD_Details(e.getKey(), e.getValue());
}
// optionally, you may Collections.sort(details); the list
In your DD_Details class, consider using a primitive double instead of a boxed Double.
I guess that your problem is in your second for-loop in the else-part of the if-statement. Even if you have dates which match you will have some dates which will not match. Thus you set back your amount the the amount of i.
You should add:
System.out.println("The dates are equal. New amount is: " + amt);
to your if-statement and:
System.out.println("Dates do not match. " + amt);
Now you should see that you add the amount the correct way but you reset it in the else part several times.

Convert recursive function into the non-recursive function

Is it possible to convert the function go into the non-recursive function? Some hints or a start-up sketch would be very helpful
public static TSPSolution solve(CostMatrix _cm, TSPPoint start, TSPPoint[] points, long seed) {
TSPSolution sol = TSPSolution.randomSolution(start, points, seed, _cm);
double t = initialTemperature(sol, 1000);
int frozen = 0;
System.out.println("-- Simulated annealing started with initial temperature " + t + " --");
return go(_cm, sol, t, frozen);
}
private static TSPSolution go(CostMatrix _cm, TSPSolution solution, double t, int frozen) {
if (frozen >= 3) {
return solution;
}
i++;
TSPSolution bestSol = solution;
System.out.println(i + ": " + solution.fitness() + " " + solution.time() + " "
+ solution.penalty() + " " + t);
ArrayList<TSPSolution> nHood = solution.nHood();
int attempts = 0;
int accepted = 0;
while (!(attempts == 2 * nHood.size() || accepted == nHood.size()) && attempts < 500) {
TSPSolution sol = nHood.get(rand.nextInt(nHood.size()));
attempts++;
double deltaF = sol.fitness() - bestSol.fitness();
if (deltaF < 0 || Math.exp(-deltaF / t) > Math.random()) {
accepted++;
bestSol = sol;
nHood = sol.nHood();
}
}
frozen = accepted == 0 ? frozen + 1 : 0;
double newT = coolingSchedule(t);
return go(_cm, bestSol, newT, frozen);
}
This is an easy one, because it is tail-recursive: there is no code between the recursive call & what the function returns. Thus, you can wrap the body of go in a loop while (frozen<3), and return solution once the loop ends. And replace the recursive call with assignments to the parameters: solution=bestSol; t=newT;.
You need to thinkg about two things:
What changes on each step?
When does the algorithm end?
Ans the answer should be
bestSol (solution), newT (t), frozen (frozen)
When frozen >= 3 is true
So, the easiest way is just to enclose the whole function in something like
while (frozen < 3) {
...
...
...
frozen = accepted == 0 ? frozen + 1 : 0;
//double newT = coolingSchedule(t);
t = coolingSchedule(t);
solution = bestSol;
}
As a rule of thumb, the simplest way to make a recursive function iterative is to load the first element onto a Stack, and instead of calling the recursion, add the result to the Stack.
For instance:
public Item recursive(Item myItem)
{
if(myItem.GetExitCondition().IsMet()
{
return myItem;
}
... do stuff ...
return recursive(myItem);
}
Would become:
public Item iterative(Item myItem)
{
Stack<Item> workStack = new Stack<>();
while (!workStack.isEmpty())
{
Item workItem = workStack.pop()
if(myItem.GetExitCondition().IsMet()
{
return workItem;
}
... do stuff ...
workStack.put(workItem)
}
// No solution was found (!).
return myItem;
}
This code is untested and may (read: does) contain errors. It may not even compile, but should give you a general idea.

Categories