One line LINQ to flatten string[] to a string? - c#

I came up with the foreach below but I am hoping this can be accomplished in one line.. maybe linq? Any ideas would be appreciated.
foreach (string item in decoder.AllKeys)
{
message += String.Format("{0}: {1} ;", item, decoder[item]);
}

var message = string.Join(
";",
decoder.AllKeys
.Select(x => string.Format("{0}: {1} ", x, decoder[item]))
.ToArray()
);

The String class from BCL already supports this. Here is a one liner to achieve this using String class. I would always use the BCL operation if it's available there considering the MS guys would have already taken the pain to optimize such a crucial class.
String.Join(";", decoder.AllKeys);
Here is the link to the MSDN for all the variants of this method - http://goo.gl/kmwfYt

If you're in .NET 4.0, you can use this:
string message = string.Join(" ;", decoder.AllKeys
.Select(k => string.Format("{0}: {1}", k, decoder[k]));
If you're not on .NET 4.0 yet, you need to convert the collection to an array:
string message = string.Join(" ;", decoder.AllKeys
.Select(k => string.Format("{0}: {1}", k, decoder[k]).ToArray());

This should work.
decoder.AllKeys.Aggregate("", (current, item) => current + String.Format("{0}: {1} ;", item, decoder[item]));

IEnumerable<string> query =
from KeyValuePair<string, string> kvp in decoder
select String.Format("{0}: {1} ;", kvp.Key, kvp.Value);
string result = string.Join(null, query);

If you already have a Join extension method on IEnumerable like I have:
public static string Join(this IEnumerable<string> values, string separator)
{
return string.Join(separator, values);
}
then the rest is just a one-liner:
decoder.AllKeys.Select(item => String.Format("{0}: {1}", item, decoder[item])).Join(";");
Note that if you're not using .NET 4, then you can replace the implementation of the extension method to whatever works for your version.
EDIT:
Even better, create the following extension method:
public static string Join(this IEnumerable<string> values, string separator, Func<string, string> selector)
{
return string.Join(separator, values.Select(selector));
}
and call it as follows:
decoder.AllKeys.Join(";", item => String.Format("{0}: {1}", item, decoder[item]));
you won't get more one-line than that, other than putting an extension method on NameValueCollection, or whatever decoder is :)

Related

Aggregating anonymous types using lambda

I'm trying to print out a list of phone numbers (\n for each number) in a format {type}:{number}. So from a list of 2 numbers I would print out:
Home: 111-111-1111
Cell: 222-222-2222
So far I can select into an anonymous type but when I go to aggregate it is leterally printing out the whole anonymous type on the screen looking exactly like below:
new { phoneType = Home + ": ", phoneNumber = 111-111-1111 }
Should I even be using aggregate?
This is what I'm working with:
PhoneNumbers.Select(x => new { phoneType = x.PhoneType, phoneNumber = x.PhoneNumber1 }).Aggregate(
(p, x) => new { phoneType = p.phoneType + ": ", x.phoneNumber });
If you just want the phone numbers as strings, you can create that string in a Select call:
var x = PhoneNumbers.Select(x => string.Format("{0}: {1}", x.PhoneType, x.PhoneNumber));
Your Aggregate call is basically a bug (assuming it even compiles) because you're combining the "current" phone number with the previous phone type. It's also unnecessary for combining the phone numbers as text because of existing string methods:
var phoneNumberStrings = PhoneNumbers.Select(x => string.Format("{0}: {1}", x.PhoneType, x.PhoneNumber));
var text = string.Join(Environment.NewLine, phoneNumberStrings);
While you could do this with IEnumerable<string>.Aggregate, I feel it's cleaner if you just use string.Join. Someone reading your code will be able to more easily get the meaning out if they see "join these strings with newline."
string.Join(
Environment.NewLine,
PhoneNumbers.Select(x =>
string.Format("{0}: {1}", x.PhoneType, x.PhoneNumber));

string.Join throws OutOfMemory Exception

I have a list of strings and I want to join them with " "(space) between them, so I use the string.Join method:
foreach (var line in lines)
{
var strings = lines.Where(l => l.Code == line.Code).Select(l => l.Data);
var original = string.Join(" ", strings);
}
Data looks something like this: "123456789, 987654321, 32132, 7873892 ..."
But I get an OutOfMemoryException. Why? each string is approximatly 100-150 characters and there are 5-10 strings in the list.
Is there a better way then the string.Join?
Try this (and let us know if you get the same error):
lines.GroupBy(l => l.Code).Select(l => string.Join(" ", l.Select (x => x.Data)));
foreach (var line in lines.GroupBy(p=>p.Code))
{
var original = string.Join(" ", line.Select(p=>p.Data));
}
The StringBuild() class can join strings and isn't immutable.
Here's an MSDN article talking about immutable string vs how StringBuilder works.
http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.71).aspx

Convert List<Enum> to List<string>

I have a list of enum values:
public static readonly List<NotifyBy> SupportedNotificationMethods = new List<NotifyBy> {
NotifyBy.Email, NotifyBy.HandHold };
I would like to output it as a comma separated list. (EG: "Email, Handhold")
What is the cleanest way of doing this?
Perhaps this:
var str = String.Join(", ", SupportedNotificationMethods.Select(s => s.ToString()));
You can read more about the String.Join method at MSDN. Older versions of String.Join don't have an overload that takes an IEnumerable. In that case just call ToArray() after select.
you can use linq:
string.Join(", ", SupportedNotificationMethods.Select(e => e.ToString());
String.Join(", ", SupportedNotificationMethods.Select(e => e.ToString()).ToArray());

Lambda expression how to perform String.Format on List<String>?

I have a list like:
List<String> test = new List<String> {"Luke", "Leia"};
I would like to use something like this:
test.Select(s => String.Format("Hello {0}", s));
but it doesn't adjust the names in the list. Is there a way to use lambda expressions to alter these? Or is it because strings are immutable that this doesn't work?
Select doesn't modify the original collection; it creates a new IEnumerable<T> that you can enumerate with a foreach or convert to a list:
List<String> test2 = test.Select(s => String.Format("Hello {0}", s)).ToList();
test still contains "Luke" and "Leia", and test2 contains "Hello Luke" and "Hello Leia".
If you want to modify the original list with a lambda expression, you can apply the lambda expression to each list item individually and store the result back in the collection:
Func<string, string> f = s => String.Format("Hello {0}", s);
for (int i = 0; i < test.Count; i++)
{
test[i] = f(test[i]);
}
Here:
for(int i = 0; i < test.Count; i++) {
test[i] = String.Format("Hello {0}", test[i]);
}
No need to be fancy. No need to abuse LINQ. Just keep it simple.
You could go one step beyond this and create an extension method like so:
static class ListExtensions {
public static void AlterList<T>(this List<T> list, Func<T, T> selector) {
for(int index = 0; index < list.Count; index++) {
list[index] = selector(list[index]);
}
}
}
Usage:
test.AlterList(s => String.Format("Hello {0}", s));
Select is for projecting and is really intended to be used in circumstances where there are no side-effects. Manipulating the items in the list very clearly has side-effects. In fact, the line
test.Select(s => String.Format("Hello {0}", s));
doesn't do anything except creating an IEnumerable<string> that could eventually be enumerated over to produce the projection.
One other possible solution:
List<String> test = new List<String> {"Luke", "Leia"};
List<string> FormattedStrings = new List<string>();
test.ForEach(testVal => FormattedStrings.Add(String.Format("Hello {0}", testVal)));
You could just do a foreach statement:
test.ForEach(s=>String.Format("Hello {0}", s));
That is if you are trying to just update the names.

C# Converting ListBox to String then Aggregate

I have a listbox being populated from a combo box. What i need to do is string all the contents of the listbox together and then Aggregate it.
string cols = listbox1.items.Aggregate((f, s) => f.value + "," + s.value);
doesnt work.
Items is an ObjectCollection, so all you know for sure is that it contains objects. You can call ToString on any object:
string[] items = listBox1.Items
.OfType<object>()
.Select(item => item.ToString())
.ToArray();
string result = string.Join(",", items);
Note that this is both more readable and more efficient than using aggregate, which causes multiple string concatenations.
pretty old thread, but here's my solution if anybody need it still
string cols = string.Join(",", listBox1.Items.Cast<String>());
Supposing that you have strings in the listbox, try this:
string cols =
String.Join(",", listbox1.Items
.OfType<Object>()
.Select(i => i.ToString())
.ToArray());
Generally String.Join is used to join a string. This is faster than using a StringBuilder as the size of the new string is already known and it doesn't have to copy everything twice.
string cols = listBox1.Items.Cast<string>().Aggregate((f, s) => f + "," + s);
I think you need to explicitly do ToString()
string cols = listbox1.items.Aggregate((f, s) => f.value.ToString() + "," + s.value.ToString());

Categories