StreamInsight -- Convert a string csv PointEvent to multiple int PointEvent's - c#

I can do it in a UDSO :
public sealed class PromoIdsToEvents : CepPointStreamOperator<string, int>
{
public override IEnumerable<int> ProcessEvent(PointEvent<string> inputEvent)
{
if (inputEvent.Payload.Length > 0)
{
string[] separator = new[] { "," };
string[] idStrings = inputEvent.Payload.Split(separator, StringSplitOptions.RemoveEmptyEntries);
foreach (var p in idStrings)
{
yield return int.Parse(p);
}
}
}
public override bool IsEmpty
{
get { return false; }
}
}
Is it possible to do it all in a query? All this does is make an IEnumberable :
var multipleEventsAsInts = from c in csvEvents
let split = c.Split(',').Select(int.Parse)
select split;

You might be able to do this work in a subject, but I think the way you are doing it, through a UDSO is the proper way to work with a StreamInsight event in a procedural manner.
By creating that UDSO, you now have a reusable chunk of code. If you just did that work in the query logic, you wouldn't have the ability to re-use it as easily.

TXPower is absolutely correct. Additionally, the UDSO will be more efficient than using a UDF (static method) in a query.
However, your sample UDSO could be a bit better ... note that the return value from ProcessEvent is an IEnumerable. You don't need to yield return one at a time; create your enumerable (an array would be fine) and then simply return it.

Related

Return Multiple Objects From Array Through a Method

The method below returns only the first object in the array.
public string PRINT_CRATE()
{
foreach (var soda in sodaCans)
{
if (soda != null)
{
return string.Join(Environment.NewLine, soda);
}
else
{
return ("Empty spot");
}
}
return null;
}
It works fine except from that I want it to "group up" all the objects and return them. As it is now, only the first object get's returned.
What is the best approach for this?
Probably the simplest thing to do what you're asking for is a Linq query:
return string.Join(Environment.NewLine, sodaCans.Select(x => x ?? "EmptySpot"));
The .Select method allows you to transform each element, and here I'm using the null coalescing operator ?? to replace null values with "EmptySpot".
if you want to return a single string with all the data put into it, you are probably looking for something like that:
public string PrintCrate()
{
string rv = "";
foreach (var soda in sodaCans)
{
if (soda != null)
{
rv += "\n" + soda;
}
}
return rv;
}
EDIT: According to the comments, my solution was sub-optimal.
I'd like to point out, that Erik's answer is probably the best solution. It is short, correctly handles the newlines, and outputs the "Empty Spot" entries as the OP has probably wanted.
Out of educational value, I'll include a version with StringBuilder and manual enumeration that is comparable with Erik's answer in that it is close to the implementation of string.Join():
public string PrintCrate()
{
using (var enumerator = sodaCans.GetEnumerator())
{
if (!enumerator.MoveNext())
return null
var stringBuilder = new StringBuilder();
stringBuilder.Append(enumerator.Current ?? "EmptySpot");
while (enumerator.MoveNext())
{
stringBuilder.Append(Environment.NewLine);
stringBuilder.Append(enumerator.Current ?? "EmptySpot");
}
return StringBuilder.ToString();
}
}

Best way of concatenating one value of objects by comma separation? C#

I found a way to do this but I just wonder if there is actually a straight way to do it.
So I have list of Artist objects and I would like to print out all the artists name as one string with comma separated.
here is my code to achieve this
string.Join(",",Artists.Select(a => a.ArtistName).ToArray());
Or I should be using ToString method?
From .NET framework version 4 onwards, this should be enough
string s = string.Join(",",Artists.Select(a => a.ArtistName));
It depends upon what one means by "best".
Generally, the way you have it is best prior to .NET 4.0, while from 4.0 on the best way is to leave out the .ToArray() because from that version on there is a form of Join that takes an IEnumerable<string> and hence you don't need to spend time and memory creating an array.
Now, if we are working prior to .NET 4.0, we can create our own version of those added with that version:
public static class StringMore
{
//You might want to use conditional statements to mark this as obsolete in 4.0 or higher
public static string Join(string separator, IEnumerable<string> values)
{
if (values == null)
throw new ArgumentNullException("values");
if (separator == null)
separator = string.Empty;
using (IEnumerator<string> en = values.GetEnumerator())
{
if (!en.MoveNext())
return "";
StringBuilder sb = new StringBuilder(en.Current);
while(en.MoveNext())
{
stringBuilder.Append(separator);
stringBuilder.Append(en.Current);
}
return sb.ToString();
}
}
public static string Join<T>(string separator, IEnumerable<T> values)
{
if (values == null)
throw new ArgumentNullException("values");
if (separator == null)
separator = string.Empty;
using (IEnumerator<string> en = values.GetEnumerator())
{
if (!en.MoveNext())
return "";
T cur = en.Current;
StringBuilder sb = cur == null ? new StringBuilder() : new StringBuilder(cur.ToString());
while(en.MoveNext())
{
stringBuilder.Append(separator);
cur = en.Current;
if(cur != null)
stringBuilder.Append(cur.ToString());
}
return sb.ToString();
}
}
}
Then you could use StringMore.Join(",",Artists.Select(a => a.ArtistName)) to get results in .NET 3.5 that are almost (not quite as we lack some caching of StringBuilders that .NET does internally) as efficient as 4.0. Whether this is "better" or not depends on whether the performance gain is worth the extra work and extra complexity to bug-checking of adding more methods, which depends on how heavy the calls are in practice (how many elements) and how often they are hit.
Your solution is the correct way. You "could" override the ToString method of your object Artists, but string.Join is the preferred way.
separateor could be ","
public static string Join<T>(this IEnumerable<T> items, string separator)
{
return items.Join(separator, i => i.ToString());
}
I'd prefer to create my own method here, in order to make my code "look better". So I would make an extension to the list of artists.
public static class ArtistListExtensions
{
public static String unionNames(this List<Artist> artists, String seperator)
{
return string.Join(seperator, artists.Select(a => a.ArtistName));
}
}
I would now simply use this extension as such:
artists.unionNames(", ");

LINQ Replacement for Nested Array Iteration and Update?

I need some help with syntactic sugar.
I have a ThisClass[3] and ThatClass[3].
public class ThisClass
{
public string Thing1;
public string Thing2;
public string Thing3;
public string Thing4;
}
public class ThatClass
{
public string Thing1;
public string Thing2;
}
Each instance in the array of ThatClass was created based on an instance in the same position of array ThisClass.
So ThatClass[0] has its fields with the same values as ThisClass[0], except it only has 2 fields instead of 4.
I would like to now update each instance in the ThisClass array, with fields from the matching index position of the object in the ThatClass array. I could do nested for loops, but I need help thinking through a LINQ option.
ThisClass[0].Thing1 = ThatClass[0].Thing1;
ThisClass[0].Thing2 = ThatClass[0].Thing2;
works but I am sure could be done better. Using C#, .NET 4.5.
I don't see any need for nested loops:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].Thing1 = thoseClasses[i].Thing1;
theseClasses[i].Thing2 = thoseClasses[i].Thing2;
}
You could potentially add a CopyFrom(ThatClass) method to ThisClass, leading to:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].CopyFrom(thoseClasses[i]);
}
... but that's all I'd do. LINQ is do to with querying, not causing side-effects... I don't think it's a good fit here.
Attention: As #Jon put, LINQ is not about causing side-effects and if you do so you may end up with a code with unexpected behavior (but it's possible).
This code does that:
ThisClass[] these = new ThisClass[100];
ThatClass[] those = new ThatClass[100];
// init these and those items
those.Zip(these, (that, #this) =>
{
#this.Thing1 = that.Thing1;
#this.Thing2 = that.Thing2;
return that;
}).ToList();
As you're asking for LINQ... this will get you an unrelated IEnumerable<ThisClass>, and will not modify the original array.
(I'm assuming that the thisClass and thatClass arrays are called thisArray and thatArray, respectively)
thisArray.Select((n, x) => { n.Thing1 = thatArray[x].Thing1; n.Thing2 = thatArray[x].Thing2; return n; }).ToArray();
(If you really wanted LINQ and assigning it, just assign it back to the original array)

Sorting a list with two parameters using CompareTo

I am presently sorting a C# list using the 'CompareTo' method in the object type contained in the list. I want to sort ascendingly all items by their WBS (Work Breakdown Structure) and I can manage this very well using the following code:
public int CompareTo(DisplayItemsEntity other)
{
string[] instanceWbsArray = this.WBS.Split('.');
string[] otherWbsArray = other.WBS.Split('.');
int result = 0;
for (int i = 0; i < maxLenght; i++)
{
if (instanceWbsArray[i].Equals(otherWbsArray[i]))
{
continue;
}
else
{
result = Int32.Parse(instanceWbsArray[i]).CompareTo(Int32.Parse(otherWbsArray[i]));
break;
}
}
return result;
}
Now, I would like to be able to sort considering more than one parameter, as in the project name alphabetically, before considering the second which would be the WBS. How can I do this?
I don't know the details of your class, so I'll provide an example using a list of strings and LINQ. OrderBy will order the strings alphabetically, ThenBy will order them by their lengths afterwards. You can adapt this sample to your needs easily enough.
var list = new List<string>
{
"Foo",
"Bar",
"Foobar"
};
var sortedList = list.OrderBy(i => i).
ThenBy(i => i.Length).
ToList();
What we generally do in cases like yours is this:
public int CompareTo( SomeClass other )
{
int result = this.SomeProperty.CompareTo( other.SomeProperty );
if( result != 0 )
return result;
result = this.AnotherProperty.CompareTo( other.AnotherProperty );
if( result != 0 )
return result;
[...]
return result;
}
P.S.
When posting code, please try to include only the code which is pertinent to your question. There is a load of stuff in the code that you posted that I did not need to read, and that in fact made my eyes hurt.
I like Eve's answer because of it's flexibility but I'm kinda surprised that no-one has mentioned creating a custom IComparer<T> instance
IComparer<T> is a generic interface that defines a method for comparing two instances of the type T. The advantage of using IComparer<T> is that you can create implementations for each sort order you commonly use and then use these as and when necessary. This allows you to create a default sort order in the types CompareTo() method and define alternative orders separately.
E.g.
public class MyComparer
: IComparer<YourType>
{
public int Compare(YourType x, YourType y)
{
//Add your comparison logic here
}
}
IComparer<T> is particularly useful for composition where you can do things like have a comparer which compares some properties of a given type using another comparer that operates on the type of the property.
It's also very useful if you ever need to define a sorting on a type you don't control. Another advantage it has is it doesn't require LINQ so can be used in older code (.Net 2.0 onwards)
First compare the project name alphabetically and if they are not equal return value, if not perform comparison based on second value
public int CompareTo(DisplayItemsEntity other)
{
if(other.ProjectName.CompareTo(this.ProjectName) != 0)
{
return other.ProjectName.CompareTo(this.ProjectName)
}
//else do the second comparison and return
return result;
}

How to properly check IEnumerable for existing results

What's the best practice to check if a collection has items?
Here's an example of what I have:
var terminalsToSync = TerminalAction.GetAllTerminals();
if(terminalsToSync.Any())
SyncTerminals(terminalsToSync);
else
GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
The GetAllTerminals() method will execute a stored procedure and, if we return a result, (Any() is true), SyncTerminals() will loop through the elements; thus enumerating it again and executing the stored procedure for the second time.
What's the best way to avoid this?
I'd like a good solution that can be used in other cases too; possibly without converting it to List.
Thanks in advance.
I would probably use a ToArray call, and then check Length; you're going to enumerate all the results anyway so why not do it early? However, since you've said you want to avoid early realisation of the enumerable...
I'm guessing that SyncTerminals has a foreach, in which case you can write it something like this:
bool any = false;
foreach(var terminal in terminalsToSync)
{
if(!any)any = true;
//....
}
if(!any)
GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
Okay, there's a redundant if after the first loop, but I'm guessing the cost of an extra few CPU cycles isn't going to matter much.
Equally, you could do the iteration the old way and use a do...while loop and GetEnumerator; taking the first iteration out of the loop; that way there are literally no wasted operations:
var enumerator = terminalsToSync.GetEnumerator();
if(enumerator.MoveNext())
{
do
{
//sync enumerator.Current
} while(enumerator.MoveNext())
}
else
GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
How about this, which still defers execution, but buffers it once executed:
var terminalsToSync = TerminalAction.GetAllTerminals().Lazily();
with:
public static class LazyEnumerable {
public static IEnumerable<T> Lazily<T>(this IEnumerable<T> source) {
if (source is LazyWrapper<T>) return source;
return new LazyWrapper<T>(source);
}
class LazyWrapper<T> : IEnumerable<T> {
private IEnumerable<T> source;
private bool executed;
public LazyWrapper(IEnumerable<T> source) {
if (source == null) throw new ArgumentNullException("source");
this.source = source;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public IEnumerator<T> GetEnumerator() {
if (!executed) {
executed = true;
source = source.ToList();
}
return source.GetEnumerator();
}
}
}
Personally i wouldnt use an any here, foreach will simply not loop through any items if the collection is empty, so i would just do it like that. However i would recommend that you check for null.
If you do want to pre-enumerate the set use .ToArray() eg will only enumerate once:
var terminalsToSync = TerminalAction.GetAllTerminals().ToArray();
if(terminalsToSync.Any())
SyncTerminals(terminalsToSync);
var terminalsToSync = TerminalAction.GetAllTerminals().ToList();
if(terminalsToSync.Any())
SyncTerminals(terminalsToSync);
else
GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
.Length or .Count is faster since it doesn't need to go through the GetEnumerator()/MoveNext()/Dispose() required by Any()
Here's another way of approaching this problem:
int count = SyncTerminals(terminalsToSync);
if(count == 0) GatewayLogAction.WriteLogInfo(Messages.NoTerminalsForSync);
where you change SyncTerminals to do:
int count = 0;
foreach(var obj in terminalsToSync) {
count++;
// some code
}
return count;
Nice and simple.
All the caching solutions here are caching all items when the first item is being retrieved. It it really lazy if you cache each single item while the items of the list are is iterated.
The difference can be seen in this example:
public class LazyListTest
{
private int _count = 0;
public void Test()
{
var numbers = Enumerable.Range(1, 40);
var numbersQuery = numbers.Select(GetElement).ToLazyList(); // Cache lazy
var total = numbersQuery.Take(3)
.Concat(numbersQuery.Take(10))
.Concat(numbersQuery.Take(3))
.Sum();
Console.WriteLine(_count);
}
private int GetElement(int value)
{
_count++;
// Some slow stuff here...
return value * 100;
}
}
If you run the Test() method, the _count is only 10. Without caching it would be 16 and with .ToList() it would be 40!
An example of the implementation of LazyList can be found here.
If you're seeing two procedure calls for the evaluation of whatever GetAllTerminals() returns, this means that the procedure's result isn't being cached. Without knowing what data-access strategy you're using, this is quite hard to fix in a general way.
The simplest solution, as you've alluded, is to copy the result of the call before you perform any other operations. If you wanted to, you could neatly wrap this behaviour up in an IEnumerable<T> which executes the inner enumerable call just once:
public class CachedEnumerable<T> : IEnumerable<T>
{
public CachedEnumerable<T>(IEnumerable<T> enumerable)
{
result = new Lazy<List<T>>(() => enumerable.ToList());
}
private Lazy<List<T>> result;
public IEnumerator<T> GetEnumerator()
{
return this.result.Value.GetEnumerator();
}
System.Collections.IEnumerable GetEnumerator()
{
return this.GetEnumerator();
}
}
Wrap the result in an instance of this type and it will not evaluate the inner enumerable multiple times.

Categories