所以,我正在将项目从PHP转换为C#。作为查询的结果获得了通用数据列表
//C# code
public class TermsCommodityModel
{
public int terms_id { get; set; }
public int commodity_id { get; set; }
public int custom { get; set; }
public int calculated { get; set; }
public string name { get; set; }
public string formula { get; set; }
public int division_id { get; set; }
}
我能够将其填充到termsTable
List<TermsCommodityModel>
。然后PHP代码开始循环termsTable
。(C#和PHP代码使用相同的变量进行简单转换)。第一行彻底改变了我的数据结构
//PHP code
if (!isset($termsTable[$name]))
$termsTable[$name] = array();
我想,奇怪但可行。然后第二个条件创建了另一个子数组,然后继续。现在PHP代码看起来如此,
//PHP Code
if (!isset($termsTable[$name][$t->commodity_id]))
$termsTable[$name][$t->commodity_id] = array();
//.Omitted for brevity
//....
$year = date("Y") + 5;
for ($y = 2008; $y<= $year; $y++) {
$termsTable[$name][$t->commodity_id][$y] = array();
for ($i=1; $i<=12; $i++)
$termsTable[$name][$t->commodity_id][$y][$i] = 0;
}
这是最终的数据结构
//PHP Code
$termsTable[$name]
$termsTable[$name][$t->commodity_id]
$termsTable[$name][$t->commodity_id][$y]
$termsTable[$name][$t->commodity_id][$y][$i]
这实际上是动态地创建了一个对象数组数组的数组。问题是PHP是一种动态类型语言。它不需要指定类型
C#中的哪个数据结构可能会这样做?不能使用tuple
,因为它们是分层的,对吧?
哪种方法可以解决这个问题?任何指针都非常有用,因为这有点重要。
答案 0 :(得分:1)
我对PHP知之甚少,但看起来我可以效仿。您在问题中演示的代码基于关联数组。在.NET中,关联数组通常通过Dictionary<TKey, TValue>
数据结构实现。
您从平面List<TermsCommodityModel>
开始,然后您可以按如下方式构建基于字典的分层结构:
// a flat list of TermsCommodityModel, filled with data elsewhere
List<TermsCommodityModel> list = new List<TermsCommodityModel>();
Dictionary<string, Dictionary<int, Dictionary<int, Dictionary<int, int>>>> termsTable = list
.GroupBy(tcm => tcm.name)
.ToDictionary(
tcmGroup => tcmGroup.Key,
tcmGroup => tcmGroup.ToDictionary(
tcm => tcm.commodity_id,
tcm => CreateYearMonthTable()));
还有一个辅助函数:
static Dictionary<int, Dictionary<int, int>> CreateYearMonthTable()
{
var year = DateTime.Now.Year + 5;
return Enumerable
.Range(2008, year - 2008 + 1)
.ToDictionary(
y => y,
y => Enumerable.Range(1, 12).ToDictionary(i => i, i => 0));
}
以下是您如何访问此数据结构中的叶值的示例:
string name = "ABC";
int commodityId = 12345;
int year = 2010;
int month = 10;
int value = termsTable[name][commodityId][year][month];
修改强>
解决问题的更好方法是我的第二个答案: https://stackoverflow.com/a/47593724/4544845
答案 1 :(得分:1)
我不确定TermsCommodityModel
与php代码有什么关系,因为据我所知,它并没有显示在任何地方。无论如何,你可以通过(ab)使用dynamic
和DynamicObject
来实现类似于php的语法。首先创建这样的类:
public class DynamicDictionary : DynamicObject {
private readonly Dictionary<object, object> _dictionary;
public DynamicDictionary() {
_dictionary = new Dictionary<object, object>();
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
// this will be called when you do myDict[index] (but not myDict[index] = something)
if (indexes.Length != 1)
throw new Exception("Only 1-dimensional indexer is supported");
var index = indexes[0];
// if our internal dictionary does not contain this key
// - add new DynamicDictionary for that key and return that
if (_dictionary.ContainsKey(index)) {
_dictionary.Add(index, new DynamicDictionary());
}
result = _dictionary[index];
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) {
// this will be called when you do myDict[index] = value
if (indexes.Length != 1)
throw new Exception("Only 1-dimensional indexer is supported");
var index = indexes[0];
// just set value
_dictionary[index] = value;
return true;
}
}
并像这样使用它:
dynamic termsTable = new DynamicDictionary();
var name = "name";
int commodityId = 123;
var year = DateTime.Now.Year + 5;
for (int y = 2008; y <= year; y++) {
for (int i = 1; i < 12; i++) {
// that's fine
termsTable[name][commodityId][y][i] = 0;
}
}
// let's see what we've got:
for (int y = 2008; y <= year; y++) {
for (int i = 1; i < 12; i++) {
// that's fine
Console.WriteLine(termsTable[name][commodityId][y][i]);
}
}
要更多地镜像您的PHP代码,请更改TryGetIndex
,如下所示:
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
// this will be called when you do myDict[index] (but not myDict[index] = something)
if (indexes.Length != 1)
throw new Exception("Only 1-dimensional indexer is supported");
var index = indexes[0];
// if our internal dictionary does not contain this key
// return null
if (!_dictionary.ContainsKey(index)) {
result = null;
}
else {
result = _dictionary[index];
}
return true;
}
然后你需要检查这个密钥是否已经存在(这在我看来有点好):
dynamic termsTable = new DynamicDictionary();
var name = "name";
int commodityId = 123;
var year = DateTime.Now.Year + 5;
// need to check if such key exists
// like isset in php
if (termsTable[name] == null)
termsTable[name] = new DynamicDictionary();
if (termsTable[name][commodityId] == null)
termsTable[name][commodityId] = new DynamicDictionary();
for (int y = 2008; y <= year; y++) {
if (termsTable[name][commodityId][y] == null)
termsTable[name][commodityId][y] = new DynamicDictionary();
for (int i = 1; i < 12; i++) {
// that's fine
termsTable[name][commodityId][y][i] = 0;
}
}
当然,通过这样做可以将类型安全抛到窗外,但如果你对此感觉不错 - 为什么不呢。
答案 2 :(得分:1)
虽然我的第一个答案中的代码再现了用PHP编写的原始逻辑,但它缺少一些非常重要的特性。这不是不言自明的,而且难以阅读。
具体来说,像Dictionary<string, Dictionary<int, Dictionary<int, Dictionary<int, int>>>>
这样的东西是一个巨大的反模式。没有人知道这个怪物数据结构的键和值的预期。这太容易出错了。
对代码进行分解的更好方法如下:
public class TermsTable
{
private readonly Dictionary<string, IndexByCommodityId> _index;
public TermsTable(IEnumerable<TermsCommodityModel> list)
{
_index = list
.GroupBy(tcm => tcm.name)
.ToDictionary(
tcmGroup => tcmGroup.Key,
tcmGroup => new IndexByCommodityId(tcmGroup));
}
public IndexByCommodityId this[string name] => _index[name];
}
public class IndexByCommodityId
{
private readonly Dictionary<int, IndexByYear> _index;
public IndexByCommodityId(IEnumerable<TermsCommodityModel> list)
{
_index = list.ToDictionary(
keySelector: tcm => tcm.commodity_id,
elementSelector: tcm => new IndexByYear());
}
public IndexByYear this[int commodityId] => _index[commodityId];
}
public class IndexByYear
{
private static readonly int _nowYear = DateTime.Now.Year;
private readonly Dictionary<int, IndexByMonth> _index;
public IndexByYear()
{
_index = Enumerable
.Range(2008, _nowYear - 2008 + 1)
.ToDictionary(
keySelector: year => year,
elementSelector: year => new IndexByMonth());
}
public IndexByMonth this[int year] => _index[year];
}
public class IndexByMonth
{
private readonly Dictionary<int, int> _index;
public IndexByMonth()
{
_index = Enumerable.Range(1, 12).ToDictionary(month => month, month => 0);
}
public int this[int month]
{
get => _index[month];
set => _index[month] = value;
}
}
使用新数据结构的代码如下所示:
// a flat list of TermsCommodityModel, filled with data elsewhere
List<TermsCommodityModel> list = new List<TermsCommodityModel>();
// create our hierarchical index from the above list
TermsTable aBetterTermsTable = new TermsTable(list);
string name = "ABC";
int commodityId = 12345;
int year = 2010;
int month = 10;
int value = aBetterTermsTable[name][commodityId][year][month];
是的,编写的代码要多得多,但值得。它更容易阅读,更不容易出错。例如,其中一个好处是IntelliSense: