Reactive Extensions Group By, Unique BufferWindow till time span with cancellation - system.reactive

I have a Hot stream of events coming of following type:
Event
{
string name;
int state ; // its 1 or 2 ie active or unactive
}
there is a function which provides parent name of given name - string GetParent(string name)
I need to buffer event per parent for 2 minutes, if during this 2 minute , i recv any event for child with state =2 for a given parent , this buffer should cancel and should output 0 otherwise i get the count of the events recvd .
I know I have to use GroupBy to partition, and then buffer and then count but i am unable to think of a way by which i create Buffer which is unique per parent, i though of using Distinct but this doesnt solve the problem, for i only dont want to create buffer till the parent is active (as once the parent's buffer gets cancelled or 2 minutes is over, the parent buffer can be created again)
So I understand I need to create a custom buffer which checks the condition for creating buffer, but how do i do this via reactive extensions.
Any help will be greatly appreciated.
regards
Thanks Brandon for your help. This is the main program I am using for testing. Its not working.As I am new to reactive extension problem can be in the way i am testing
namespace TestReactive
{
class Program
{
static int abc = 1;
static void Main(string[] args)
{
Subject<AEvent> memberAdded = new Subject<AEvent>();
//ISubject<AEvent, AEvent> syncedSubject = new ISubject<AEvent, AEvent>();
var timer = new Timer { Interval = 5 };
timer.Enabled = true;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, memberAdded);
var bc = memberAdded.Subscribe();
var cdc = memberAdded.GroupBy(e => e.parent)
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer1 = Observable.Timer(TimeSpan.FromSeconds(1));
var activeCount = children.TakeUntil(timer1).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count });
});
Observable.ForEachAsync(cdc, x => WriteMe("Dum Dum " + x.ParentName+x.Count));
// group.Dump("Dum");
Console.ReadKey();
}
static void WriteMe(string sb)
{
Console.WriteLine(sb);
}
static void MyElapsedMethod(object sender, ElapsedEventArgs e, Subject<AEvent> s)
{
AEvent ab = HelperMethods.GetAlarm();
Console.WriteLine(abc + " p =" + ab.parent + ", c = " + ab.name + " ,s = " + ab.state);
s.OnNext(ab);
}
}
}
public static AEvent GetAlarm()
{
if (gp> 4)
gp = 1;
if (p > 4)
p = 1;
if (c > 4)
c = 1;
AEvent a = new AEvent();
a.parent = "P" + gp + p;
a.name = "C" + gp + p + c;
if (containedKeys.ContainsKey(a.name))
{
a.state = containedKeys[a.name];
if (a.state == 1)
containedKeys[a.name] = 2;
else
containedKeys[a.name] = 1;
}
else
{
containedKeys.TryAdd(a.name, 1);
}
gp++; p++; c++;
return a;
}
So this method , generates a event for Parent at each tick. It generates event for parent P11,P22,P33,P44 with State =1 and then followed by events for Parent P11,P22,P33,P44 with State =2
I am using Observable.ForEach to print the result, I see its being called 4 times and after that its nothing, its like cancellation of group is not happening

Assuming that a two minute buffer for each group should open as soon as the first event for that group is seen, and close after two minutes or a zero state is seen, then I think the following works:
public static IObservable<EventCount> EventCountByParent(
this IObservable<Event> source, IScheduler scheduler)
{
return Observable.Create<EventCount>(observer => source.GroupByUntil(
evt => GetParent(evt.Name),
evt => evt,
group =>
#group.Where(evt => evt.State == 2)
.Merge(Observable.Timer(
TimeSpan.FromMinutes(2), scheduler).Select(_ => Event.Null)))
.SelectMany(
go =>
go.Aggregate(0, (acc, evt) => (evt.State == 2 ? 0 : acc + 1))
.Select(count => new EventCount(go.Key, count))).Subscribe(observer));
}
With EventCount (implementing equality overrides for testing) as:
public class EventCount
{
private readonly string _name;
private readonly int _count;
public EventCount(string name, int count)
{
_name = name;
_count = count;
}
public string Name { get { return _name; } }
public int Count { get { return _count; } }
public override string ToString()
{
return string.Format("Name: {0}, Count: {1}", _name, _count);
}
protected bool Equals(EventCount other)
{
return string.Equals(_name, other._name) && _count == other._count;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((EventCount) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_name != null ? _name.GetHashCode() : 0)*397) ^ _count;
}
}
}
And Event as:
public class Event
{
public static Event Null = new Event(string.Empty, 0);
private readonly string _name;
private readonly int _state;
public Event(string name, int state)
{
_name = name;
_state = state;
}
public string Name { get { return _name; } }
public int State { get { return _state; } }
}
I did a quick (i.e. not exhaustive) test with Rx-Testing:
public class EventCountByParentTests : ReactiveTest
{
private readonly TestScheduler _testScheduler;
public EventCountByParentTests()
{
_testScheduler = new TestScheduler();
}
[Fact]
public void IsCorrect()
{
var source = _testScheduler.CreateHotObservable(
OnNext(TimeSpan.FromSeconds(10).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(20).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(30).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(40).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(50).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(60).Ticks, new Event("B", 2)),
OnNext(TimeSpan.FromSeconds(70).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(140).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(150).Ticks, new Event("A", 1)));
var results = _testScheduler.CreateObserver<EventCount>();
var sut = source.EventCountByParent(_testScheduler).Subscribe(results);
_testScheduler.Start();
results.Messages.AssertEqual(
OnNext(TimeSpan.FromSeconds(60).Ticks, new EventCount("B", 0)),
OnNext(TimeSpan.FromSeconds(130).Ticks, new EventCount("A", 4)),
OnNext(TimeSpan.FromSeconds(260).Ticks, new EventCount("A", 2)));
}
}

something like....
source.GroupBy(e => GetParent(e.name))
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer = Observable.Timer(TimeSpan.FromMinutes(2));
var activeCount = children.TakeUntil(timer).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count };
});
This will give you a sequence of { ParentName, Count } objects.

Related

Translating java DFS algorithm code to Dart

I've been doing some research to find a suitable algorithm for suggesting friends. I came across DFS, but I've never implemented it in Dart before. Could someone please help me t translate it into Dart? Below is the java code:
public class SuggestFriendsDFS<T> {
private HashMap<T, ArrayList<T>> adj = new HashMap<>(); //graph
private List<Set<T>> groups = new ArrayList<>();
public void addFriendship(T src, T dest) {
adj.putIfAbsent(src, new ArrayList<T>());
adj.get(src).add(dest);
adj.putIfAbsent(dest, new ArrayList<T>());
adj.get(dest).add(src);
}
//V is total number of people, E is number of connections
private void findGroups() {
Map<T, Boolean> visited = new HashMap<>();
for (T t: adj.keySet())
visited.put(t, false);
for (T t:adj.keySet()) {
if (!visited.get(t)) {
Set<T> group = new HashSet<>();
dfs(t, visited, group);
groups.add(group);
}
}
}
//DFS + memoization
private void dfs(T v, Map<T, Boolean> visited, Set<T> group ) {
visited.put(v,true);
group.add(v);
for (T x : adj.get(v)) {
if (!visited.get(x))
dfs(x, visited, group);
}
}
public Set<T> getSuggestedFriends (T a) {
if (groups.isEmpty())
findGroups();
Set<T> res = new HashSet<>();
for (Set<T> t : groups) {
if (t.contains(a)) {
res = t;
break;
}
}
if (res.size() > 0)
res.remove(a);
return res;
}
}
I'm aware it's too much to ask, but any help will be much appreciated as I tried to translate it and ended up getting loads of errors. Thanks in advance!(: For reference, this is where I found the explanation for the java code.
I tried https://sma.github.io/stuff/java2dartweb/java2dartweb.html that does automatic Java to Dart conversion but it doesn't work well as soon as the code is a bit complex.
See the full conversion below, you can try it in Dartpad
import 'dart:collection';
class SuggestFriendsDFS<T> {
final HashMap<T, List<T>> _adj = HashMap(); //graph
final List<Set<T>> groups = [];
//Time O(1), Space O(1)
void addFriendship(T src, T dest) {
_adj.putIfAbsent(src, () => <T>[]);
_adj[src]!.add(dest);
_adj.putIfAbsent(dest, () => <T>[]);
_adj[dest]!.add(src);
}
//DFS wrapper, Time O(V+E), Space O(V)
//V is total number of people, E is number of connections
void findGroups() {
Map<T, bool> visited = HashMap();
for (T t in _adj.keys) {
visited[t] = false;
}
for (T t in _adj.keys) {
if (visited[t] == false) {
Set<T> group = HashSet();
_dfs(t, visited, group);
groups.add(group);
}
}
}
//DFS + memoization, Time O(V+E), Space O(V)
void _dfs(T v, Map<T, bool> visited, Set<T> group) {
visited[v] = true;
group.add(v);
for (T x in _adj[v] ?? []) {
if ((visited[x] ?? true) == false) _dfs(x, visited, group);
}
}
//Time O(V+E), Space O(V)
Set<T> getSuggestedFriends(T a) {
if (groups.isEmpty) findGroups();
var result = groups.firstWhere((element) => element.contains(a),
orElse: () => <T>{});
if (result.isNotEmpty) result.remove(a);
return result;
}
}
void main() {
SuggestFriendsDFS<String> g = SuggestFriendsDFS();
g.addFriendship("Ashley", "Christopher");
g.addFriendship("Ashley", "Emily");
g.addFriendship("Ashley", "Joshua");
g.addFriendship("Bart", "Lisa");
g.addFriendship("Bart", "Matthew");
g.addFriendship("Christopher", "Andrew");
g.addFriendship("Emily", "Joshua");
g.addFriendship("Jacob", "Christopher");
g.addFriendship("Jessica", "Ashley");
g.addFriendship("JorEl", "Zod");
g.addFriendship("KalEl", "JorEl");
g.addFriendship("Kyle", "Lex");
g.addFriendship("Kyle", "Zod");
g.addFriendship("Lisa", "Marge");
g.addFriendship("Matthew", "Lisa");
g.addFriendship("Michael", "Christopher");
g.addFriendship("Michael", "Joshua");
g.addFriendship("Michael", "Jessica");
g.addFriendship("Samantha", "Matthew");
g.addFriendship("Samantha", "Tyler");
g.addFriendship("Sarah", "Andrew");
g.addFriendship("Sarah", "Christopher");
g.addFriendship("Sarah", "Emily");
g.addFriendship("Tyler", "Kyle");
g.addFriendship("Stuart", "Jacob");
g.findGroups();
print(g.groups);
String name = "Andrew";
print("Suggestion friends of " +
name +
": " +
g.getSuggestedFriends(name).toString());
}

Converting event based API to Rx.Net

I'm trying to convert an existing event-based API to a Reactive Observable API. The concrete API I'm working with is the NSNetServiceBrowser in Xamarin.iOS. This API let you browse for network devices using Zeroconf/Bonjour. However, the question would apply to any API of this kind.
The NsNetServiceBrowser offers various events of interest:
- FoundService
- NotSearched
- ServiceRemoved
The FoundService event is raised when a service is discovered, and the NotSearched is raised when the search fails.
I would like to combine the FoundService and NotSerched events, into an observable of NSNetService.
My current implementation looks like this:
public IObservable<NSNetService> Search()
{
var foundObservable = Observable
.FromEventPattern<NSNetServiceEventArgs>(
h => serviceBrowser.FoundService += h,
h => serviceBrowser.FoundService -= h)
.Select(x => x.EventArgs);
var notSearchedObservable = Observable
.FromEventPattern<NSNetServiceErrorEventArgs>(
h => serviceBrowser.NotSearched += h,
h => serviceBrowser.NotSearched -= h)
.Select(x => x.EventArgs);
var serviceObservable = Observable.Create(
(IObserver<NSNetServiceEventArgs> observer) =>
{
notSearchedObservable.Subscribe(n =>
{
string errorMessage = $"Search for {serviceType} failed:";
foreach (var kv in n.Errors)
{
log.Error($"\t{kv.Key}: {kv.Value}");
errorMessage += $" ({kv.Key}, {kv.Value})";
}
observer.OnError(new Exception(errorMessage));
});
foundObservable.Subscribe(observer);
return System.Reactive.Disposables.Disposable.Empty;
}).Select(x => x.Service);
serviceBrowser.SearchForServices(serviceType, domain);
return serviceObservable;
}
The code looks clunky and I have a gut feeling I'm not using System.Reactive correctly? Is there a more elegant way to combine event pairs, where one is producing and the other is signaling error? This is a common pattern in existing event based APIs in .NET.
Here is a small console app (depending only on System.Reactive) illustrating the type of API I want to Reactify:
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace ReactiveLearning
{
class Program
{
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var foundSub = observableFound.Subscribe(s =>
{
Console.WriteLine($"Found service: {s.Name}");
}, () =>
{
Console.WriteLine("Found Completed");
});
var errorSub = observableError.Subscribe(e =>
{
Console.WriteLine("ERROR!");
}, () =>
{
Console.WriteLine("Error Completed");
});
browser.Search();
Console.ReadLine();
foundSub.Dispose();
errorSub.Dispose();
Console.WriteLine();
}
}
class ServiceBrowser
{
public EventHandler<ServiceFoundEventArgs> ServiceFound;
public EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search()
{
Task.Run(async () =>
{
for (var i = 0; i < 5; ++i)
{
await Task.Delay(1000);
ServiceFound?.Invoke(this, new ServiceFoundEventArgs(new Service($"Service {i}")));
}
var r = new Random();
if (r.NextDouble() > 0.5)
{
ServiceError?.Invoke(this, new ServiceSearchErrorEventArgs());
}
});
}
}
class ServiceFoundEventArgs : EventArgs
{
public Service Service { get; private set; }
public ServiceFoundEventArgs(Service service) => Service = service;
}
class ServiceSearchErrorEventArgs : EventArgs {}
class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name { get; private set; }
public string Address { get; private set; }
public Service(string name) => Name = name;
public void ResolveAddress()
{
Task.Run(async () =>
{
await Task.Delay(500);
var r = new Random();
if (r.NextDouble() > 0.5)
{
Address = $"http://{Name}.com";
AddressResolved?.Invoke(this, EventArgs.Empty);
}
else
{
ErrorResolvingAddress?.Invoke(this, EventArgs.Empty);
}
});
}
}
}
Thank you for the excellent sample code. You need to make use of the excellent Materialize & Dematerialize operators. Here's how:
var observableFoundWithError =
observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x =>
Notification
.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
using (observableFoundWithError.Subscribe(
s => Console.WriteLine($"Found service: {s.Name}"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
The Materialize() operator turns an IObservable<T> into and IObservable<Notification<T>> which allows the standard OnError and OnCompleted to be emitted through the OnNext call. You can use Notification.CreateOnError<T>(new Exception("Error")) to construct elements of observable which you can turn back into an IObservable<T> with Dematerialize().
I've thrown the Synchronize() to ensure that you've created a valid observable. The use of Materialize() does let you construct observables that don't follow the regular observable contract. Part of what Synchronize() does is just ensure only one OnError and only one OnCompleted and drops any OnNext that comes after either of the two.
Try this as a way to do what you wanted in the comments:
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var observableFoundWithError = observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
Func<Service, IObservable<Service>> resolveService = s =>
Observable.Create<Service>(o =>
{
var observableResolved = Observable.FromEventPattern<EventArgs>(
h => s.AddressResolved += h,
h => s.AddressResolved -= h);
var observableResolveError = Observable.FromEventPattern<EventArgs>(
h => s.ErrorResolvingAddress += h,
h => s.ErrorResolvingAddress -= h);
var observableResolvedWithError =
observableResolved
.Select(x => s)
.Materialize()
.Merge(
observableResolveError
.Do(e => Console.WriteLine($"Error resolving: {s.Name}"))
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception($"Error resolving address for service: {s.Name}"))))
.Dematerialize()
.Synchronize();
s.ResolveAddress();
return observableResolvedWithError.Subscribe(o);
});
using (
observableFoundWithError
.Select(s => resolveService(s))
.Switch()
.Subscribe(
s => Console.WriteLine($"Found and resolved service: {s.Name} ({s.Address})"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
}
public class ServiceBrowser
{
public event EventHandler<ServiceFoundEventArgs> ServiceFound;
public event EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search() { }
}
public class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name;
public string Address;
public void ResolveAddress() { }
}
public class ServiceFoundEventArgs : EventArgs
{
public Service Service;
}
public class ServiceSearchErrorEventArgs : EventArgs
{
}
I might require a bit of tweaking - perhaps an Observable.Delay in there. Let me know if it works.

Java 8 group by sum condition

I have an object Officer
public class Officer {
private String name;
private int totalDaysInOffice;
public Officer(String name, int totalDaysInOffice) {
this.name = name;
this.totalDaysInOffice = totalDaysInOffice;
}
#Override
public String toString() {
return "Officer{" +
"name='" + name + '\'' +
", totalDaysInOffice=" + totalDaysInOffice +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTotalDaysInOffice() {
return totalDaysInOffice;
}
public void setTotalDaysInOffice(int totalDaysInOffice) {
this.totalDaysInOffice = totalDaysInOffice;
}
}
Here each officer have spent days in office(just made up variable).
What I want to do is the divide the officers once I have the sum of days 10000 in separate list
Based on example below , I want to have list with
one list with John , Matthew , and Robert since they sum to more 10K
One list with Patrick as he has 10K
Dave would be in separate list.
I have tried group by but not sure how can I add this condition.
public class OffierExample {
public static void main(String[] args) {
List<Officer> officerList = new ArrayList<>();
officerList.add(new Officer("John",5000));
officerList.add(new Officer("Matthew",3000));
officerList.add(new Officer("Robert",2000));
officerList.add(new Officer("Dave",2000));
officerList.add(new Officer("Patrick",10000));
Map<Officer, Integer> collect = officerList.stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(Officer::getTotalDaysInOffice)));
System.out.println(collect);
}
}
Is there anyways it can be done in Java 8
**
*****UPDATE*****
**
I have achieved using traditional loop but I want to use Java 8 group by if possible
public class OffierExample {
public static void main(String[] args) {
List<Officer> officerList = new ArrayList<>();
officerList.add(new Officer("John", 5000));
officerList.add(new Officer("Matthew", 3000));
officerList.add(new Officer("Robert", 2000));
officerList.add(new Officer("Dave", 2000));
officerList.add(new Officer("Patrick", 10000));
officerList.add(new Officer("YYYY", 600));
officerList.add(new Officer("XXXX", 600));
//keep totalDaysInOfficeSum
int totalDaysInOfficeSum = 0;
//the final list
List<List<Officer>> off = Lists.newArrayList();
//the working list
List<Officer> tempOffList = Lists.newArrayList();
for (Officer officer : officerList) {
//get sum
totalDaysInOfficeSum = totalDaysInOfficeSum + officer.getTotalDaysInOffice();
//if sum is more than 10K or equal
if (totalDaysInOfficeSum >= 10000) {
//add it in temp list
tempOffList.add(officer);
//add in master list
off.add(tempOffList);
//reset temp list
tempOffList = new ArrayList<>();
//reset sum
totalDaysInOfficeSum = 0;
continue;
}
//add in temp list
tempOffList.add(officer);
}
//any left over
if (!tempOffList.isEmpty()) {
off.add(tempOffList);
}
//printint out
System.out.println("Officers list =" + off.size());
off.forEach(o -> {
System.out.println("List size:" + o.size());
o.forEach(oo -> {
System.out.println(oo.getName() + "::" + oo.getTotalDaysInOffice());
});
System.out.println("====================");
});
}
}
Output
Officers list =3
List size:3
John::5000
Matthew::3000
Robert::2000
====================
List size:2
Dave::2000
Patrick::10000
====================
List size:2
YYYY::600
XXXX::600
====================
Something like this:
List<List<Officer>> result = officerList.stream().collect(Collector.of(
() -> new ArrayList<List<Officer>>(),
(list, entry) -> {
if (list.size() == 0) {
List<Officer> inner = new ArrayList<>();
inner.add(entry);
list.add(inner);
} else {
List<Officer> last = list.get(list.size() - 1);
int sum = last.stream().mapToInt(Officer::getTotalDaysInOffice).sum();
if (sum < 10_000) {
last.add(entry);
} else {
List<Officer> inner = new ArrayList<>();
inner.add(entry);
list.add(inner);
}
}
},
(left, right) -> {
throw new IllegalArgumentException("Not for parallel");
}));
Here is the solution with my library:
MutableInt sum = MutableInt.of(0);
List<List<Officer>> off = Stream.of(officerList)
.splitToList(officer -> sum.getAndSet(sum.value() < 10000 ? sum.value() + officer.getTotalDaysInOffice() : 0) < 10000)
.toList();
Or:
List<List<Officer>> off = Seq.of(officerList)
.split(officer -> sum.getAndSet(sum.value() < 10000 ? sum.value() + officer.getTotalDaysInOffice() : 0) < 10000);
Java 8 way
List<Officer> officerList = new ArrayList<>();
officerList.add(new Officer("John", 5000));
officerList.add(new Officer("Matthew", 3000));
officerList.add(new Officer("Robert", 2000));
officerList.add(new Officer("Dave", 2000));
officerList.add(new Officer("Patrick", 10000));
List<List<Officer>> separatedOfficerLists = officerList.stream()
.sorted(Comparator.comparing(Officer::getTotalDaysInOffice).reversed())
.collect(Collectors.groupingByConcurrent(o -> {
int totalDays = o.getTotalDaysInOffice();
int divisor = (totalDays / 10000) + 1;
return (divisor * 10000) - totalDays;
})).entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
System.out.println(separatedOfficerLists);

Rx.NET batching by condition, min and max timer

I'm doing batching of messages in stream want to do it by 3 conditions:
if batcher can't add incoming message (for whatever internal logic of batcher)
if there were messages and then no messages within X seconds (basically what throttle does)
if there is continious stream of messages more often then every X seconds, then after Y seconds still close batch (cap on throttle)
I need to be able to change X and Y seconds in runtime without losing current batch (doesn't matter if it is closed immediately on config change or by closing conditions).
Condition function and batch process function should not run in parallel threads.
I'm using Rx-Main 2.2.5.
So far I came up with solution below and it seems to work, but I think there may be much simpler solution with reactive extensions?
Also with this solution capTimer doesn't restart if closing condition is "batcher can't add this message".
Extension:
public static class ObservableExtensions
{
public static IDisposable ConditionalCappedThrottle<T>(this IObservable<T> observable,
int throttleInSeconds,
int capTimeInSeconds,
Func<T, bool> conditionFunc,
Action capOrThrottleAction,
Action<T, Exception> onException,
T fakeInstance = default(T))
{
Subject<T> buffer = new Subject<T>();
var capTimerObservable = new Subject<long>();
var throttleTimerObservable = observable.Throttle(TimeSpan.FromSeconds(throttleInSeconds)).Select(c => 1L);
IDisposable maxBufferTimer = null;
var bufferTicks = observable
.Do(c =>
{
if (maxBufferTimer == null)
maxBufferTimer = Observable.Timer(TimeSpan.FromSeconds(capTimeInSeconds))
.Subscribe(x => capTimerObservable.OnNext(1));
})
.Buffer(() => Observable.Amb(
capTimerObservable
.Do(c => Console.WriteLine($"{DateTime.Now:mm:ss.fff} cap time tick closing buffer")),
throttleTimerObservable
.Do(c => Console.WriteLine($"{DateTime.Now:mm:ss.fff} throttle time tick closing buffer"))
))
.Do(c =>
{
maxBufferTimer?.Dispose();
maxBufferTimer = null;
})
.Where(changes => changes.Any())
.Subscribe(dataChanges =>
{
buffer.OnNext(fakeInstance);
});
var observableSubscriber = observable.Merge(buffer)
.Subscribe(subject =>
{
try
{
if (!subject.Equals(fakeInstance)) {
if (conditionFunc(subject))
return;
Console.WriteLine($"{DateTime.Now:mm:ss.fff} condition false closing buffer");
maxBufferTimer?.Dispose();
}
capOrThrottleAction();
if (!subject.Equals(fakeInstance))
conditionFunc(subject);
}
catch (Exception ex)
{
onException(subject, ex);
}
});
return new CompositeDisposable(maxBufferTimer, observableSubscriber);
}
}
And usage:
class Program
{
static void Main(string[] args)
{
messagesObs = new Subject<Message>();
new Thread(() =>
{
while (true)
{
Thread.Sleep(random.Next(3) * 1000);
(messagesObs as Subject<Message>).OnNext(new Message());
}
}).Start();
while (true)
{
throttleTime = random.Next(8) + 2;
maxThrottleTime = random.Next(10) + 20;
Console.WriteLine($"{DateTime.Now:mm:ss.fff} resubscribing with {throttleTime} - {maxThrottleTime}");
Subscribe();
Thread.Sleep((random.Next(10) + 60) * 1000);
}
}
static Random random = new Random();
static int throttleTime = 3;
static int maxThrottleTime = 10;
static IDisposable messagesSub;
static IObservable<Message> messagesObs;
static void Subscribe()
{
messagesSub?.Dispose();
BatchProcess();
messagesSub = messagesObs.ConditionalCappedThrottle(
throttleTime,
maxThrottleTime,
TryAddToBatch,
BatchProcess,
(msg, ex) => { },
new FakeMessage());
}
static bool TryAddToBatch(Message msg)
{
if (random.Next(100) > 85)
{
Console.WriteLine($"{DateTime.Now:mm:ss.fff} can't add to batch");
return false;
}
else
{
Console.WriteLine($"{DateTime.Now:mm:ss.fff} added to batch");
return true;
}
}
static void BatchProcess()
{
Console.WriteLine($"{DateTime.Now:mm:ss.fff} Processing");
Thread.Sleep(2000);
Console.WriteLine($"{DateTime.Now:mm:ss.fff} Done Processing");
}
}
public class Message { }
public class FakeMessage : Message { }
Tests I want to work:
public class Test
{
static Subject<Base> sub = new Subject<Base>();
static int maxTime = 19;
static int throttleTime = 6;
// Batcher.Process must be always waited before calling any next Batcher.Add
static void MaxTime()
{
// foreach on next Batcher.Add must be called
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
// Process must be called after 19 seconds = maxTime
}
static void Throttle()
{
// foreach on next Batcher.Add must be called
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
// Process must be called after 5.9+5.9+6 seconds = throttleTime
}
static void Condition()
{
// foreach on next Batcher.Add must be called
sub.OnNext(new A());
Thread.Sleep(6 * 1000 - 100);
sub.OnNext(new B());
// Process must be called because Batcher.Add will return false
// Batcher.Add(B) must be called after Process
}
static void MaxTimeOrThorttleNotTickingRandomly()
{
sub.OnNext(new A());
// Process called by throttle condition in 6 seconds
Thread.Sleep(1000 * 100);
// Process is not called for remaining 94 seconds
sub.OnNext(new A());
// Process called by throttle condition in 6 seconds
}
static void Resub()
{
sub.OnNext(new A());
sub.OnNext(new A());
sub.OnNext(new A());
sub.OnNext(new A());
sub.OnNext(new A());
maxTime = 15;
throttleTime = 3;
// Process is called
// Resubs with new timinig conditions
sub.OnNext(new A());
// Process called by throttle condition in 3 seconds
}
}
public class Batcher
{
private Type batchingType;
public bool Add(Base element)
{
if (batchingType == null || element.GetType() == batchingType)
{
batchingType = element.GetType();
return true;
}
return false;
}
public void Process()
{
batchingType = null;
}
}
public class Base{}
public class A : Base { }
public class B : Base { }

How I count all the number of records in a RecordStore

I have a LWUIT app that should display the number of records in a LWUIT list.
To get all the records I use a method called getRecordData() that returns all records as a String array, it works fine.
But how do I count the number of these records?
import java.util.*;
import com.sun.lwuit.events.*;
import javax.microedition.midlet.*;
import com.sun.lwuit.*;
import com.sun.lwuit.plaf.*;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms .*;
public class number_of_records extends MIDlet {
private RecordStore recordStore;
// Refresh2( ) method for getting the time now
public String Refresh2()
{
java.util.Calendar calendar = java.util.Calendar.getInstance();
Date myDate = new Date();
calendar.setTime(myDate);
StringBuffer time = new StringBuffer();
time.append(calendar.get(java.util.Calendar.HOUR_OF_DAY)).append(':');
time.append(calendar.get(java.util.Calendar.MINUTE)) ;
// time.append(calendar.get(java.util.Calendar.SECOND));
String tt = time.toString();
return tt;
}
// return all records of recordStore RecordStore
public String [] getRecordData( )
{
String[] str = null;
int counter = 0;
try
{
RecordEnumeration enumeration = recordStore.enumerateRecords(null, null, false);
str = new String[recordStore.getNumRecords()];
while(enumeration.hasNextElement())
{
try
{
str[counter] = (new String(enumeration.nextRecord()));
counter ++;
}
catch(javax.microedition.rms.RecordStoreException e)
{
}
}
}
catch(javax.microedition.rms.RecordStoreNotOpenException e)
{
}
catch(java.lang.NullPointerException n)
{
}
return str;
}
public void startApp()
{
com.sun.lwuit.Display.init(this);
final Button addition = new Button("add a goal");
final com.sun.lwuit.TextField tf = new com.sun.lwuit.TextField();
final com.sun.lwuit.List mylist = new com.sun.lwuit.List();
final Button All = new Button("All Goals");
final com.sun.lwuit.Form ff = new com.sun.lwuit.Form();
final com.sun.lwuit.Form g = new com.sun.lwuit.Form();
ff.getStyle().setBgColor(0X99CCFF);
All.getStyle().setBgColor(0X0066CC);
Style g_style5 = g.getSelectedStyle() ;
g.addComponent(tf);
g.addComponent(addition);
addition.getStyle().setBgColor(0X0066CC);
g.addComponent(All);
g.getStyle().setBgColor(0X99CCFF);
addition.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
//
String s =tf.getText();
if( s!=null && s.length() > 0)
{
try
{
// Store the time in the String k
String k = Refresh2();
// The record and the time stored in KK String
String kk =tf.getText()+"-"+k;
// Add an item (the kk String) to mylist List.
mylist.addItem(kk);
byte bytestream[] = kk.getBytes() ;
// Add a record to recordStore.
int i = recordStore.addRecord(bytestream, 0, bytestream.length);
}
catch(Exception ex) { }
// Inform the User that he added the a record.
Dialog validDialog = new Dialog(" ");
Style Dialogstyle = validDialog.getSelectedStyle() ;
validDialog.setScrollable(false);
validDialog.getDialogStyle().setBgColor(0x0066CC);
validDialog.setTimeout(1000); // set timeout milliseconds
TextArea textArea = new TextArea("...."); //pass the alert text here
textArea.setFocusable(false);
textArea.setText("A goal has been added"+"" );
validDialog.addComponent(textArea);
validDialog.show(0, 10, 10, 10, true);
}
// Information to user that he/she didn’t add a record
else if((s==null || s.length()<= 0))
{
Dialog validDialo = new Dialog(" ");
validDialo.setScrollable(false);
validDialo.getDialogStyle().setBgColor(0x0066CC);
validDialo.setTimeout(5000); // set timeout milliseconds
TextArea textArea = new TextArea("...."); //pass the alert text here
textArea.setFocusable(false);
textArea.setText("please enter scorer name or number");
validDialo.addComponent(textArea);
validDialo.show(50, 50, 50, 50, true);
}
}
});
/*Action here for displaying all records of recordStore RecordStore in a new form */
All.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try
{
recordStore = RecordStore.openRecordStore("My Record Store", true);
}
catch(Exception ex) {}
try
{
com.sun.lwuit.Label l = new com.sun.lwuit.Label(" Team Goals") ;
ff.addComponent(l);
// Store the records of recordStore in string array
String [] record= getRecordData();
int j1;
String valueToBeInserted2="";
int k=getRecordData().length;
for( j1=0;j1< getRecordData().length;j1++)
{
valueToBeInserted2=valueToBeInserted2 + " " + record[j1];
if(j1==getRecordData().length)
{
mylist.addItem(record[j1]);
int m = getRecordData().length;
// Counting the number of records
String goals =""+getRecordData().length;
/* I tried to use for…loop to count them by length of the recordStore and render it.
This list also should display the number of records on the form.
But it didn’t !!!
*/
mylist.addItem(goals);
}
}
ff.addComponent(mylist);
}
catch(java.lang.IllegalArgumentException e)
{
}
finally
{
ff.show();
}
}
}
);
g.show();
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional) {
}
}
I Wrote this code but it gives NullPointerException at recordStore.enumerateRecords (null, null,true);
So I think the problem here.
please help.
myButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvet av)
{
try
{
RecordEnumeration enumeration = recordStore.enumerateRecords (null, null,true);
int o =recordStore.getNumRecords () ;
}
catch(Exception e)
{
}
}
});
what you need is enumeration.numRecords(); i reckon recordStore.getNumRecords() should work also, since this is what you are using the populate the array, you could even use the length of the array itself. These options are all in the code, it would be better to explore a bit more and also check the documentation to resolve trivial problems.
you could use the length of the array or set a RecordListener to your recordstore and increase a counter when added a record to recordstore.
here is the solution of my problem , I do a for loop to get the number of
elements of the array.
the counter should be the length of array
count.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent av)
{
try
{
recordStore = RecordStore.openRecordStore("recordStore", true);
}
catch(Exception e)
{ }
try
{
RecordEnumeration enumeration = recordStore.enumerateRecords (null, null,true);
}
catch(Exception e)
{
}
String record[] = getRecordData();
int j;
j = record.length-1;
Dialog validDialog = new Dialog(" ");
Style Dialogstyle = validDialog.getSelectedStyle() ;
validDialog.setScrollable(false);
validDialog.getDialogStyle().setBgColor(0x0066CC);
validDialog.setTimeout(1000); // set timeout milliseconds
TextArea textArea = new TextArea("....");
textArea.setFocusable(false);
textArea.setText("Number Counted"+j );
validDialog.addComponent(textArea);
validDialog.show(0, 10, 10, 10, true);
}});