Dictionary<string,string>
,并想用这种模式将其压平:编写这样的扩展方法:{key}={value}|{key}={value}|{key}={value}|
是否可以用LINQ解决这个问题?
#1 楼
这样的东西应该可以工作:public static string ToString(this Dictionary<string,string> source, string keyValueSeparator, string sequenceSeparator)
{
if (source == null)
throw new ArgumentException("Parameter source can not be null.");
var pairs = source.Select(x => string.Format("{0}{1}{2}", x.Key, keyValueSeparator, x.Value));
return string.Join(sequenceSeparator, pairs);
}
评论
\ $ \ begingroup \ $
啊,.Select方法返回一个IEnumerable
\ $ \ endgroup \ $
–马丁
2012年2月15日在11:37
\ $ \ begingroup \ $
String.format使用StringBuilder,但在这种情况下,它将为每个枚举创建一个新实例。 Trevor的解决方案当然更快,但是除非您的字典太大,否则我不认为您需要担心此处的性能:)
\ $ \ endgroup \ $
–马蒂亚斯
2012年2月15日在12:42
\ $ \ begingroup \ $
您不需要String.Format即可进行简单的字符串连接,而没有它,您的代码将更快。只需执行x.Key + keyValueSeparator + x.Value,编译器就会将其转换为String.Concat(),后者仅分配一个新字符串。
\ $ \ endgroup \ $
–克里斯·桑蒂(Chris Sainty)
2012年2月21日在21:24
\ $ \ begingroup \ $
@Mattias仅供参考,并不重要,但是您似乎在ToArray()调用之前在var pair行上缺少尾括号...
\ $ \ endgroup \ $
– dreza
2012年2月21日在23:15
\ $ \ begingroup \ $
@Mattias在发布到这里之前,您已经测试过代码了吗?它在.NET 4上给出了编译错误。
\ $ \ endgroup \ $
– Tomas
2012年8月30日12:23
#2 楼
实际上,您可以使用LINQ的Aggregate方法内联所有这些功能。return d.Aggregate(new StringBuilder(), (sb, x) => sb.Append(x.Key + keySep + x.Value + pairSep), sb => sb.ToString(0, sb.Length - 1));
假设您可以阅读LINQ,它可能是最干净的。但这不是最快的。我尝试了所有提出的解决方案,而Mattias的答案实际上是迄今为止提出的最快的解决方案。
我有一个更快的解决方案。 br />多次调用Append的速度更快,并且像在我的Aggregate(更改为执行多个附加操作)中那样扩展运行它也更快。
当然,两者之间的性能差异所有这些都可以忽略不计。因此,选择一个最容易理解的代码,其他代码查看者会更好地理解它。
评论
\ $ \ begingroup \ $
假设pairSep只有一个字符长。要考虑不同的长度,应将其更改为返回sb.ToString(0,sb.Length-pairSep.Length);
\ $ \ endgroup \ $
– chrisofspades
14-10-17在18:56
#3 楼
Chris Sainty的答案可能是最快的,但这是最短的-根据要求使用Linq:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static class DictionaryExtension
{
public static string ToStringFlattened(this Dictionary<string, string> source, string keyValueSeparator="=", char sequenceSeparator='|')
{
return source == null ?
"" : source.Aggregate("", (str, v) =>
str + v.Key
+ keyValueSeparator
+ v.Value
+ sequenceSeparator)
.TrimEnd(sequenceSeparator);
}
}
static void Main()
{
Console.WriteLine(
new Dictionary<string, string>()
{ { "key1", "val1" }, { "key2", "value2" }, }.ToStringFlattened()
);
}
在.Net Framework> = 4.0中,您可以仅使用Zip:
public static string ToStringFlattened(this Dictionary<string, string> source, string keyValueSeparator="=", string sequenceSeparator="|")
{
return source == null ? "" : string.Join(sequenceSeparator, source.Keys.Zip(source.Values, (k, v) => k + keyValueSeparator + v));
}
注意:如果要在最后保留sequenceSeparator,请从第一个方法中删除Trim,然后将sequenceSeparator添加('+')到第二个例子。
评论
\ $ \ begingroup \ $
很好,但是str + = v.Key应该是str + v.Key,因为我们没有修改str,我们只是在为其传播一个新值
\ $ \ endgroup \ $
– Aluan Haddad
16-09-22在5:18
\ $ \ begingroup \ $
感谢Aluan!原则上你是对的。实际上,在这种情况下,几乎没有什么区别:在您的情况下,生成的已编译IL代码将是传递给string.Concat(string,string,string,string,string)的四重字符串声明,在所述情况下是四重字符串数组,随后调用string.Concat(string [])。
\ $ \ endgroup \ $
–洛伦茨·罗索尔(Lorenz Lo Sauer)
16-9-29在5:12
\ $ \ begingroup \ $
的确如此,因为突变是无关紧要的。我没有编辑的声誉,但是您在示例中使用=而不是加号引入了一个错误。
\ $ \ endgroup \ $
– Aluan Haddad
16-9-29在6:30
\ $ \ begingroup \ $
当然。谢谢。对于错字,它也应显示出令人关注的速度差异。否则,如所示,没有任何内容。
\ $ \ endgroup \ $
–洛伦茨·罗索尔(Lorenz Lo Sauer)
16-09-29在7:01
#4 楼
您是否特别需要将其作为字符串使用,还是只想将字典作为设置存储在项目属性的“应用程序设置”部分中?如果仅需要将字典存储在设置文件中,则以下代码也可以满足您的需要:(无论是什么),将其序列化,函数将返回表示该对象的字符串,如下所示:字符串到
Serialize
,它将返回一个对象。您将需要使用原始类型,例如:public static string Serialize(object obj)
{
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, obj);
byte[] mStream = memorystream.ToArray();
string slist = Convert.ToBase64String(mStream);
return slist;
}
public static object Unserialize(string str)
{
byte[] mData = Convert.FromBase64String(str);
MemoryStream memorystream = new MemoryStream(mData);
BinaryFormatter bf = new BinaryFormatter();
Object obj = bf.Deserialize(memorystream);
return obj;
}
#5 楼
首先,如果尚未创建ForEach扩展方法,请添加一个:
public static class EnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
}
}
}
public static class DictionaryExtensions
{
public static string ToString<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary, string keyValueSeparator, string sequenceSeparator)
{
var stringBuilder = new StringBuilder();
dictionary.ForEach(
x => stringBuilder.AppendFormat("{0}{1}{2}{3}", x.Key.ToString(), keyValueSeparator, x.Value.ToString(), sequenceSeparator));
return stringBuilder.ToString(0, stringBuilder.Length - sequenceSeparator.Length);
}
}
然后调用它:
var dictionary = new Dictionary<string, string>();
dictionary.Add("key1", "value1");
dictionary.Add("key2", "value2");
dictionary.Add("key3", "value3");
System.Console.WriteLine(dictionary.ToString("=", "|"));
评论
\ $ \ begingroup \ $
顺便说一下,List
\ $ \ endgroup \ $
– Lars-Erik
2012年2月15日14:10
\ $ \ begingroup \ $
@ Lars-Erik-在IEnumerable
\ $ \ endgroup \ $
–特雷弗·皮利(Trevor Pilley)
2012-2-15 14:22
\ $ \ begingroup \ $
@TrevorPilley如果性能确实是一个问题,我同意。如果是这样,我认为您还必须考虑一些其他事项-它何时执行,如果您伪装了IQueryable等,该怎么办。
\ $ \ endgroup \ $
– Lars-Erik
2012-2-15 14:44
#6 楼
也可以使用StringBuilder进行纯LINQ: public static string ToStringLinq<TKey, TValue>(this Dictionary<TKey, TValue> source, string keyValueSeparator, string sequenceSeparator)
{
return source.Aggregate(new StringBuilder(),
(acc, pair) => acc.AppendFormat("{0}{1}{2}{3}", pair.Key, keyValueSeparator, pair.Value, sequenceSeparator),
builder => builder.Length > sequenceSeparator.Length ?
builder.ToString(0, builder.Length - sequenceSeparator.Length)
: String.Empty
);
}
#7 楼
纯粹的LINQ方式是通过Aggregate
扩展方法实现的:字符串。或者:
public static string ToStringLinq<TKey, TValue> (this Dictionary<TKey, TValue> source, string keyValueSeparator, string sequenceSeparator)
{
return source.Aggregate(string.Empty, (acc, pair) => string.Format("{0}{1}{2}{3}{4}", acc, sequenceSeparator, pair.Key, keyValueSeparator, pair.Value));
}
这会将分隔符放在结尾而不是开头。
如果没有前导或尾随的分隔符很重要,则可以肯定地更新lambda以解决累加器为空的情况,或者可以将代码添加到种子中,并为第一个元素添加字符串,然后在其余部分进行聚合字典,如下所示:
public static string ToStringLinq<TKey, TValue> (this Dictionary<TKey, TValue> source, string keyValueSeparator, string sequenceSeparator)
{
return source.Aggregate(string.Empty, (acc, pair) => string.Format("{0}{1}{2}{3}{4}", acc, pair.Key, keyValueSeparator, pair.Value, sequenceSeparator));
}
当然,这也遇到了Mattias回答的相同问题,由于重复
string.Format
,它会做更多的对象创建调用,但是对于性能不是问题的情况,单个聚合函数调用可以是解决问题的非常紧凑的方法。#8 楼
来自Mattias解决方案:string.Concat()
快于string.Format()
无需在
string.Join()
之前构建阵列public static string ToString(this Dictionary<string,string> source, string keyValueSeparator, string sequenceSeparator)
{
if (source == null) throw new ArgumentException("Parameter source can not be null.");
var pairs = source.Select(x => string.Concat(x.Key, keyValueSeparator, x.Value));
return string.Join(sequenceSeparator, pairs);
}
评论
我建议在Exception中声明相关参数,如下所示:throw new ArgumentException(“ Parameter不能为空。”,“ source”)@LoSauer很好,但更好的是抛出新的ArgumentException(“ Parameter不能为null。”,nameof(source));