How to get the average of a generic array list in java? - class

i'm having trouble getting the average of a generic array list of type T.

You should use <T extends Number> generic signature to specify the type of the Number types, plus, you should use instanceof keyword. A simple dummy demo here;
Test Code
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<Integer>();
List<Double> doubleList = new ArrayList<Double>();
List<Float> floatList = new ArrayList<Float>();
for(int i = 0; i < 10; i++)
{
integerList.add(new Integer(i+1));
doubleList.add(new Double(i+1));
floatList.add(new Float(i+1));
}
Utility<Integer> utilityInteger = new Utility<Integer>(integerList);
Utility<Double> utilityDouble = new Utility<Double>(doubleList);
Utility<Float> utilityFloat = new Utility<Float>(floatList);
System.out.println("Integer average: " + utilityInteger.getAverage());
System.out.println("Double average : " + utilityDouble.getAverage());
System.out.println("Float average : " + utilityFloat.getAverage());
}
public static class Utility<T extends Number>
{
// Fields
private List<T> list;
private Object average;
// Constructor
#SuppressWarnings("unchecked")
public Utility(List<T> list)
{
this.list = list;
T sample = list.get(0);
if(sample instanceof Double)
{
doAverageDouble((List<Double>) list);
}
else if (sample instanceof Integer)
{
doAverageInteger((List<Integer>) list);
}
else if (sample instanceof Float)
{
doAverageFloat((List<Float>) list);
}
else
{
throw new IllegalStateException("Constructor must be initialiez with either of Double, Integer or Float list");
}
}
// Methods
private void doAverageDouble(List<Double> list) {
Double sum = new Double(0);
for(Double d : list)
{
sum += d;
}
average = sum/new Double(list.size());
}
private void doAverageInteger(List<Integer> list) {
Integer sum = new Integer(0);
for(Integer d : list)
{
sum += d;
}
average = sum/new Integer(list.size());
}
private void doAverageFloat(List<Float> list) {
Float sum = new Float(0);
for(Float d : list)
{
sum += d;
}
average = sum/new Float(list.size());
}
Object getAverage()
{
return average;
}
}
}
Console Output
Integer average: 5
Double average : 5.5
Float average : 5.5

Related

Avro serialize and desiaralize List<UUID>

I cannot understand how to serialize List to binary format and deserialize back to List. I have tried to use CustomEncoding for this purpose:
public class ListUUIDAsListStringEncoding extends CustomEncoding<List<UUID>> {
{
schema = Schema.createArray(Schema.createUnion(Schema.create(Schema.Type.STRING)));
schema.addProp("CustomEncoding", "com.my.lib.common.schemaregistry.encoding.ListUUIDAsListStringEncoding");
}
#Override
protected void write(Object datum, Encoder out) throws IOException {
var list = (List<UUID>) datum;
out.writeArrayStart();
out.setItemCount(list.size());
for (Object r : list) {
if (r instanceof UUID) {
out.startItem();
out.writeString(r.toString());
}
}
out.writeArrayEnd();
}
#Override
protected List<UUID> read(Object reuse, Decoder in) throws IOException {
var newArray = new ArrayList<UUID>();
for (long i = in.readArrayStart(); i != 0; i = in.arrayNext()) {
for (int j = 0; j < i; j++) {
newArray.add(UUID.fromString(in.readString()));
}
}
return newArray;
}
}
'write' method seems to pass correctly, but 'read' method stoped with exception 'java.lang.ArrayIndexOutOfBoundsException: 36' when trying to read string.
What I do wrong and how to deserialize data correctly?
Solved myself:
Put my encoding class here if someone will need it:
public class ListUuidAsNullableListStringEncoding extends CustomEncoding<List<UUID>> {
{
schema = Schema.createUnion(
Schema.create(Schema.Type.NULL),
Schema.createArray(Schema.create(Schema.Type.STRING))
);
}
#Override
protected void write(Object datum, Encoder out) throws IOException {
if (datum == null) {
out.writeIndex(0);
out.writeNull();
} else {
out.writeIndex(1);
out.writeArrayStart();
out.setItemCount(((List) datum).size());
for (Object item : (List) datum) {
if (item instanceof UUID) {
out.startItem();
out.writeString(item.toString());
}
}
out.writeArrayEnd();
}
}
#Override
protected List<UUID> read(Object reuse, Decoder in) throws IOException {
switch (in.readIndex()) {
case 1:
var newArray = new ArrayList<UUID>();
for (long i = in.readArrayStart(); i != 0; i = in.arrayNext()) {
for (int j = 0; j < i; j++) {
newArray.add(UUID.fromString(in.readString()));
}
}
return newArray;
default:
in.readNull();
return null;
}
}
}

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);

Creating custom plugin for chinese tokenization

I'm working towards properly integrating the stanford segmenter within SOLR for chinese tokenization.
This plugin involves loading other jar files and model files. I've got it working in a crude manner by hardcoding the complete path for the files.
I'm looking for methods to create the plugin where the paths need not be hardcoded and also to have the plugin in conformance with the SOLR plugin architecture. Please let me know if there are any recommended sites or tutorials for this.
I've added my code below :
public class ChineseTokenizerFactory extends TokenizerFactory {
/** Creates a new WhitespaceTokenizerFactory */
public ChineseTokenizerFactory(Map<String,String> args) {
super(args);
assureMatchVersion();
if (!args.isEmpty()) {
throw new IllegalArgumentException("Unknown parameters: " + args);
}
}
#Override
public ChineseTokenizer create(AttributeFactory factory, Reader input) {
Reader processedStringReader = new ProcessedStringReader(input);
return new ChineseTokenizer(luceneMatchVersion, factory, processedStringReader);
}
}
public class ProcessedStringReader extends java.io.Reader {
private static final int BUFFER_SIZE = 1024 * 8;
//private static TextProcess m_textProcess = null;
private static final String basedir = "/home/praveen/PDS_Meetup/solr-4.9.0/custom_plugins/";
static Properties props = null;
static CRFClassifier<CoreLabel> segmenter = null;
private char[] m_inputData = null;
private int m_offset = 0;
private int m_length = 0;
public ProcessedStringReader(Reader input){
char[] arr = new char[BUFFER_SIZE];
StringBuffer buf = new StringBuffer();
int numChars;
if(segmenter == null)
{
segmenter = new CRFClassifier<CoreLabel>(getProperties());
segmenter.loadClassifierNoExceptions(basedir + "ctb.gz", getProperties());
}
try {
while ((numChars = input.read(arr, 0, arr.length)) > 0) {
buf.append(arr, 0, numChars);
}
} catch (IOException e) {
e.printStackTrace();
}
m_inputData = processText(buf.toString()).toCharArray();
m_offset = 0;
m_length = m_inputData.length;
}
#Override
public int read(char[] cbuf, int off, int len) throws IOException {
int charNumber = 0;
for(int i = m_offset + off;i<m_length && charNumber< len; i++){
cbuf[charNumber] = m_inputData[i];
m_offset ++;
charNumber++;
}
if(charNumber == 0){
return -1;
}
return charNumber;
}
#Override
public void close() throws IOException {
m_inputData = null;
m_offset = 0;
m_length = 0;
}
public String processText(String inputText)
{
List<String> segmented = segmenter.segmentString(inputText);
String output = "";
if(segmented.size() > 0)
{
output = segmented.get(0);
for(int i=1;i<segmented.size();i++)
{
output = output + " " +segmented.get(i);
}
}
System.out.println(output);
return output;
}
static Properties getProperties()
{
if (props == null) {
props = new Properties();
props.setProperty("sighanCorporaDict", basedir);
// props.setProperty("NormalizationTable", "data/norm.simp.utf8");
// props.setProperty("normTableEncoding", "UTF-8");
// below is needed because CTBSegDocumentIteratorFactory accesses it
props.setProperty("serDictionary",basedir+"dict-chris6.ser.gz");
props.setProperty("inputEncoding", "UTF-8");
props.setProperty("sighanPostProcessing", "true");
}
return props;
}
}
public final class ChineseTokenizer extends CharTokenizer {
public ChineseTokenizer(Version matchVersion, Reader in) {
super(matchVersion, in);
}
public ChineseTokenizer(Version matchVersion, AttributeFactory factory, Reader in) {
super(matchVersion, factory, in);
}
/** Collects only characters which do not satisfy
* {#link Character#isWhitespace(int)}.*/
#Override
protected boolean isTokenChar(int c) {
return !Character.isWhitespace(c);
}
}
You can pass the argument through the Factory's args parameter.

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);
}});

Reactive Extensions Group By, Unique BufferWindow till time span with cancellation

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.