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(", ");
Related
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();
}
}
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.
How would you refactor this method?
private bool IsTermExist(string word)
{
var query = from term in m_xdoc.Descendants("Term")
where term.Attribute("Name").Value.ToUpper().Contains(word.ToUpper())
select term;
return query.Any();
}
I would probably use the overload of Any which accepts a predicate:
private bool TermExists(string word)
{
return m_xdoc.Descendants("Term")
.Any(term => term.Attribute("Name").Value
.IndexOf(word, StringComparison.OrdinalIgnoreCase) >= 0);
}
Notes:
Using ToUpper for case-insensitivity is generally fraught (e.g. due to behaviour in Turkish which is unexpected to most developers); using the overload of IndexOf which takes a StringComparison is preferred. (There's a similar overload for Equals.)
If we were going to compare with word.ToUpper(), we could extract it so that word.ToUpper() is only executed once, instead of once per Term element. (Micro-optimization aside, I think it's generally a nice idea to extract the iteration-invariant parts of a query first. But don't expect consistency on this front :)
This code will still go bang for Term elements without Name attributes. That may or may not be what you want.
If you want to return the first matching item instead how about a method that either returns an item or null if not found:
private XElement? GetTerm(string word)
{
var query = from term in m_xdoc.Descendants("Term")
where term.Attribute("Name").Value.ToUpper().Contains(word.ToUpper())
select term;
return query.FirstOrDefault();
}
I'd probably write this:
private static bool MatchesName(XElement el, string name)
{
var attr = el.Attribute("Name");
return attr.Value.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0;
}
private bool TermExists(string word)
{
return m_xdoc.Descendants("Term").Any(e => MatchesName(e, word));
}
Hard to say where to split off the helper method without seeing other code, though.
By fastest I mean what is the most performant means of converting each item in List to type int using C# assuming int.Parse will work for every item?
You won't get around iterating over all elements. Using LINQ:
var ints = strings.Select(s => int.Parse(s));
This has the added bonus it will only convert at the time you iterate over it, and only as much elements as you request.
If you really need a list, use the ToList method. However, you have to be aware that the performance bonus mentioned above won't be available then.
If you're really trying to eeke out the last bit of performance you could try doing someting with pointers like this, but personally I'd go with the simple linq implementation that others have mentioned.
unsafe static int ParseUnsafe(string value)
{
int result = 0;
fixed (char* v = value)
{
char* str = v;
while (*str != '\0')
{
result = 10 * result + (*str - 48);
str++;
}
}
return result;
}
var parsed = input.Select(i=>ParseUnsafe(i));//optionally .ToList() if you really need list
There is likely to be very little difference between any of the obvious ways to do this: therefore go for readability (one of the LINQ-style methods posted in other answers).
You may gain some performance for very large lists by initializing the output list to its required capacity, but it's unlikely you'd notice the difference, and readability will suffer:
List<string> input = ..
List<int> output = new List<int>(input.Count);
... Parse in a loop ...
The slight performance gain will come from the fact that the output list won't need to be repeatedly reallocated as it grows.
I don't know what the performance implications are, but there is a List<T>.ConvertAll<TOutput> method for converting the elements in the current List to another type, returning a list containing the converted elements.
List.ConvertAll Method
var myListOfInts = myListString.Select(x => int.Parse(x)).ToList()
Side note: If you call ToList() on ICollection .NET framework automatically preallocates an
List of needed size, so it doesn't have to allocate new space for each new item added to the list.
Unfortunately LINQ Select doesn't return an ICollection (as Joe pointed out in comments).
From ILSpy:
// System.Linq.Enumerable
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return new List<TSource>(source);
}
// System.Collections.Generic.List<T>
public List(IEnumerable<T> collection)
{
if (collection == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
ICollection<T> collection2 = collection as ICollection<T>;
if (collection2 != null)
{
int count = collection2.Count;
this._items = new T[count];
collection2.CopyTo(this._items, 0);
this._size = count;
return;
}
this._size = 0;
this._items = new T[4];
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
this.Add(enumerator.Current);
}
}
}
So, ToList() just calls List constructor and passes in an IEnumerable.
The List constructor is smart enough that if it is an ICollection it uses most efficient way of filling a new instance of List
Say I have a List of objects, and the object has a string property. I want to get a single comma-separated list of the value of each string property of each object in the list.
Here's 1 way to do it (sans linq)
StringBuilder result = new StringBuilder()
foreach(myObject obj in myList)
{
result.Append(obj.TheString);
result.Append(", ");
}
// then trim the trailing ", " and call ToString() on result, etc, etc...
Here's my first shot at linqification. Is there a better way?
string result = string.Join(", ", myList.Select(myObj => myObj.TheString).ToArray());
That's one line of code, but it doesn't look very efficient to me -- iterate the list just to build an array, just to iterate the array and build a string... whew!
Is there a better way?
If you want efficient, use Enumerable.Aggregate with StringBuilder:
string result = myList.Aggregate(new StringBuilder(),
(sb, o) => sb.Append(o.TheString).Append(", "))
.ToString();
The original problem is that String.Join wants an array. In .NET 4, there will be an overload that takes IEnumerable<string> (and I expect it will be implemented like above).
I like this extension method for joining strings. It's basically the same technique you are using, but wrapped in an extension method. I wouldn't recommend it for large sets since efficiency was not the goal. The benefit to me is expressiveness (very linqy) and convenient for small sets:
[Test]
public void Should_make_comma_delimited_list()
{
var colors = new List<HasColor>
{
new HasColor { Color = "red" },
new HasColor { Color = "green" },
new HasColor { Color = "blue" }
};
var result = colors.Implode(x => x.Color, ", ");
Assert.That(result, Is.EqualTo("red, green, blue"));
}
public class HasColor
{
public string Color { get; set; }
}
public static class LinqExtensions
{
public static string Implode<T>(this IEnumerable<T> list, Func<T, string> func, string separator)
{
return string.Join(separator, list.Select(func).ToArray());
}
}
Use string.Join, it's good enough.
Optimize when profiler will tell you to do so.
Readability of the version with StringBuilder is poor and you are still getting your trailing comma.
Here's another way (result is a StringBuilder):
myList.ForEach(
myObj =>
{
if (result.Length != 0)
result.Append(",");
result.Append(myObj.TheString);
}
);