将一个匿名类型的List<object>转换为Dictionary<string, SortedList<DateTime, double>>

3
我有一个函数,它创建了一个本地的List<object>,其中对象是匿名类型。我需要将这些结果返回到Dictionary<string, SortedList<DateTime, double>>中。
列表中的数据如下所示。
{ Security = "6752 JT", Date = {1/17/2011 12:00:00 AM}, zScore = 1 }
{ Security = "6753 JT", Date = {1/17/2011 12:00:00 AM}, zScore = 2 }
{ Security = "6754 JT", Date = {1/17/2011 12:00:00 AM}, zScore = 3 }
{ Security = "6752 JT", Date = {1/18/2011 12:00:00 AM}, zScore = 1 }
{ Security = "6753 JT", Date = {1/18/2011 12:00:00 AM}, zScore = 2 }
{ Security = "6754 JT", Date = {1/18/2011 12:00:00 AM}, zScore = 3 }
{ Security = "6752 JT", Date = {1/19/2011 12:00:00 AM}, zScore = 1 }
{ Security = "6753 JT", Date = {1/19/2011 12:00:00 AM}, zScore = 2 }
{ Security = "6754 JT", Date = {1/19/2011 12:00:00 AM}, zScore = 3 }

我希望使用LINQ将这些结果放入Dictionary<string, SortedList<DateTime, double>>中,其中字典的键是Security,值是包含该证券所有日期/ z-score值的SortedList。

当它是自定义对象时,我可以在LINQ中执行此操作,但是如何在匿名类型对象中执行此操作?

注意:此查询最初以不必要的复杂方式发布,并且措辞不当。这可能是为什么没有人回答它的原因!我包含了链接,以防您想查看输出为什么是这种形式。
C# LINQ Z-Score query output to a Dictionary<string, SortedList<DateTime, double>>


你的意思是说你有一个函数,它返回匿名类型作为对象,因为该类型不能被公开?如果是这样,那么我强烈建议在任何时候传递结果从一个函数到另一个函数时创建一个命名类型。 - Chris Pitman
1
我认为你正在寻找一种将类型转换回匿名类型并使属性可用的方法?请参阅https://dev59.com/rHM_5IYBdhLWcg3wXx1N以获取更多信息。 - Elian Ebbing
1
如果你需要在多个方法中使用匿名类型,我建议从中创建一个命名类型/类,并以强类型传递结果 - 其他任何方法都只是对糟糕设计的权宜之计。 - BrokenGlass
@Elian 是的,那就是问题所在。这些属性无法查询。 - Andre P.
可能是返回匿名类型?的重复问题。 - Gert Arnold
显示剩余4条评论
3个回答

8

基本上,您正在询问如何从object中取消匿名类型的装箱?

首先,我建议不要使用List<object>,而是创建一个自定义类。

public class SecurityScore {
    public string Security { get; set; }
    public DateTime Date { get; set; }
    public int ZScore { get; set; }
}

然而,如果由于某种原因您需要这样做,请尝试Jon Skeet的方法:

我一直知道通过声明该方法将返回对象,很容易返回匿名类型的实例。然而,在今天之前,我还没有想到过您实际上可以在之后转换回该类型。当然,您不能只使用普通的强制转换表达式 - 这需要在编译时已知类型的名称。但是您可以在泛型方法中进行转换...并且您可以使用类型推断来提供类型参数...如果属性的顺序、名称和类型相同,则两个匿名类型实例创建表达式将在同一程序集中使用相同的类型。

如果您想探索他的解决方案,请查看他关于该主题的博客帖子

为了完整起见,我将在此处发布他的代码:

static class GrottyHacks
{
    internal static T Cast<T>(object target, T example)
    {
        return (T) target;
    }
}

class CheesecakeFactory
{
    static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate" };
    }

    static void Main()
    {
        object weaklyTyped = CreateCheesecake();
        var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

        Console.WriteLine("Cheesecake: {0} ({1})",
            stronglyTyped.Fruit, stronglyTyped.Topping);            
    }
}

我必须承认,虽然我不是很喜欢对匿名类型进行装箱/拆箱的想法,但他的方法非常棒,而且占用的代码行数相对较少。
所以,既然我给你提供了一个可能的解决方案,我必须问一下——为什么你要这样做,而不是创建一个简单的类呢?
另外,为了完整起见,这是我如何使用Jon Skeet的解决方案来实现你的问题:
void Main()
{
    // Create a list of (boxed) anonymous objects
    var securitiesBoxed = new List<object>() {
        new { Security = "6752 JT", Date = DateTime.Parse("1/17/2011 12:00:00 AM"), zScore = 1 },
        new { Security = "6753 JT", Date = DateTime.Parse("1/17/2011 12:00:00 AM"), zScore = 2 },
        new { Security = "6754 JT", Date = DateTime.Parse("1/17/2011 12:00:00 AM"), zScore = 3 },
        new { Security = "6752 JT", Date = DateTime.Parse("1/18/2011 12:00:00 AM"), zScore = 1 },
        new { Security = "6753 JT", Date = DateTime.Parse("1/18/2011 12:00:00 AM"), zScore = 2 },
        new { Security = "6754 JT", Date = DateTime.Parse("1/18/2011 12:00:00 AM"), zScore = 3 },
        new { Security = "6752 JT", Date = DateTime.Parse("1/19/2011 12:00:00 AM"), zScore = 1 },
        new { Security = "6753 JT", Date = DateTime.Parse("1/19/2011 12:00:00 AM"), zScore = 2 },
        new { Security = "6754 JT", Date = DateTime.Parse("1/19/2011 12:00:00 AM"), zScore = 3 }
    };

    // Now, to convert to a Dictionary<string, SortedList<DateTime, double>>...
    var securitiesUnboxed = securitiesBoxed.Select(x => Cast(x, new { Security = "", Date = new DateTime(), zScore = 0 }))
        .GroupBy(x => x.Security)
        .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Date));
}

// This is the static method that will cast our anonymous type
internal static T Cast<T>(object target, T example)
{
    return (T) target;
}

LINQPad 中,上述代码的结果如下所示:

linqified data


感谢您的有益回复。"......为什么不创建一个简单的类,而要这样做呢?"我也曾问过自己同样的问题。当时,我认为t是一个中间步骤,我会在处理完成后格式化结果。回想起来,那似乎是个愚蠢的想法。两个更简单的解决方案是创建一个自定义类,或者在计算过程中迭代将各个结果添加到字典中。 - Andre P.
我必须说Jon的解决方案非常好。感谢您指出这一点。 - Andre P.
@Andre - 很高兴能帮到你!别担心——我也经历过很多这样的“噢”时刻!只需看看我的问题历史记录——我曾经问过如何反转字典(字典没有顺序)——但这就是我们学习的方式;-)而且你说得对,Jon的解决方案真的很酷! - Pandincus
我发布了一个备选方案,使用dynamic - 当.NET 4.0是一个选项时非常有用。 - Ahmad Mageed

4

Pandincus在他的回答中提供了一种可能的解决方案。如果您正在使用.NET 4.0并且可以利用 dynamic 类型,则可以提供另一种占用空间更小的解决方案:

// list is a List<object>
var query = list.Cast<dynamic>()
                .GroupBy(o => o.Security)
                .ToDictionary(o => o.Key,
                    o => new SortedList<DateTime, double>(o.ToDictionary(x => (DateTime)x.Date, x => (double)x.zScore)));

foreach (var item in query)
{
    Console.WriteLine("Security: " + item.Key);
    foreach (var kvp in item.Value)
        Console.WriteLine("Date: {0}, zScore: {1}", kvp.Key, kvp.Value);
}

这仍然感觉很不专业,理想情况下,由于您正在生成结果,应通过重新考虑方法并避免将项目添加到 List<object> 中来避免混乱。看起来您在 回答原问题时已经这样做了!

在.NET 4.0中使用dynamic是一个好主意 - 我完全错过了它! - Pandincus
@Ahmed 感谢您提供的解决方案。由于此项目的目标是3.5,我尚未使用4.0,但它确实很有趣。 - Andre P.

2

这未经过测试。

 Dictionary<string, SortedList<DateTime, double>> dict = yourList.ToDictionary(kv => kv.Security , kv => new KeyValuePair(kv.Date, kv.zScore));

根据下面的评论,您可以尝试使用 List< SomeType > 替代 List< object >

class SomeType
{
    public string Security {get;set;}
    public DateTime Date {get;set;}
    public int zScore {get;set;}
}

var results = new List<SomeType>(); 
results.Add(new { Security = secRank.Symbol, Date = sec.First().Date, zScore = zScore });

谢谢您的回复。这正是我在寻找的答案类型,但它生成了与我的版本相同的错误。即“'object'不包含'Security'的定义,也没有接受'type object'作为第一个参数的扩展方法'Security'(您是否缺少使用指令或程序集引用?)” - Andre P.
你能否张贴检索列表代码?我是指匿名类型列表的清单? - Sanjeevakumar Hiremath
该函数创建了一个列表 var results = new List<object>();,然后在这一行中填充了数据 results.Add(new { Security = secRank.Symbol, Date = sec.First().Date, zScore = zScore }); - Andre P.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接