简体   繁体   English

如何填补列表中属性值的空白?

[英]How can I fill the gaps in attribute values in the list?

I have a list: 我有一个清单:

List<BtnCountViews> btnCountViewsList;

The BtnCountViews class looks like this: BtnCountViews类如下所示:

public class BtnCountViews
{
    public int DayOfYear { get; set; }
    public int BtnCount { get; set; }
    public int Views { get; set; }
}

I have a rather unusual requirement and I am not sure how to go about starting to implement it. 我有一个非常不寻常的要求,我不确定如何着手执行它。

What I would like to do is to fill in the btnCountViewsList with `BtnCountViews for the missing DayOfYear with objects that have a BtnCount of 0 and Views of 0. 我想做的是用缺少BtnCount为0且Views为0的对象的`BtnCountViews'来填充btnCountViewsList以用于缺少的DayOfYear。

To give me a start can anyone tell me how I can find the min and max DayOfYear in the btnCountViewsList . 首先,谁能告诉我如何在btnCountViewsList中找到最小和最大btnCountViewsList Note I tagged this with LINQ but I'm not sure if this is the best tool to use. 注意我用LINQ标记了这个,但是我不确定这是否是最好的工具。

Also would be happy if someone can suggest a way to fill in the missing objects but that's not really the focus of this question as I think I need to find out how to get the min and max first. 如果有人可以提出一种方法来填充丢失的对象,这也将很高兴,但这并不是这个问题的重点,因为我认为我需要先了解如何获得最小和最大。

This is working on linqpad: 这在linqpad上工作:

Int32 max = 0, min = 0;
btnCountViewsList.ForEach(x => {
     min = Math.Min(x.Views, min);
     max = Math.Max(x.Views, max);
});

You can add missing days without finding min and max explicitly: 您可以添加缺少的天数而无需明确地找到minmax

  • Sort the list by DayOfYear in ascending order ( how? ) DayOfYear升序对列表进行排序( 如何?
  • Start a loop index i at the end of the list, and work your way backward; 在列表的末尾开始循环索引i ,然后向后移动; stop when i reaches zero i达到零时停止
  • Compare DayOfYear attribute at i and i-1 比较ii-1 DayOfYear属性
  • If the two days differ by one, move down to the next i 如果两天相差一个,向下移动到下一个i
  • Otherwise insert a new record with DayOfYear set to that of btnCountViewsList[i] minus one. 否则,插入DayOfYear设置为btnCountViewsList[i]减一的新记录。

At the end of this process your list would contain entries for each value of DayOfYear . 在此过程结束时,您的列表将包含DayOfYear每个值的DayOfYear Here is a sample implementation: 这是一个示例实现:

items.Sort((x, y) => x.DayOfYear.CompareTo(y.DayOfYear));
Console.WriteLine("Before: {0}", string.Join(", ", items.Select(x => x.DayOfYear)));
int i = items.Count-1;
while (i > 0) {
    if (items[i].DayOfYear == items[i-1].DayOfYear+1) {
        i--;
    } else {
        items.Insert(i, new BtnCountViews { DayOfYear = items[i].DayOfYear-1 });
    }
}

Demo. 演示

What I would like to do is to fill in the btnCountViewsList with `BtnCountViews for the missing DayOfYear with objects that have a BtnCount of 0 and Views of 0. 我想做的是用缺少BtnCount为0且Views为0的对象的`BtnCountViews'来填充btnCountViewsList,以用于缺少的DayOfYear。

My suggestion is that we don't try to find the missing days, we create all: 我的建议是,我们不要尝试查找丢失的日子,而是创建所有内容:

BtnCountViews[] arr = new BtnCountViews[365]; // or 366?

// suppose DayOfYear begins with 0.
for (int i = 0; i < arr.Length; i++)
{
    arr[i] = new BtnCountViews { DayOfYear = i };
}
foreach (BtnCountViews item in btnCountViewsList)
{
    arr[item.DayOfYear].BtnCount = item.BtnCount;
    arr[item.DayOfYear].Views = item.Views;
}

then arr is what you want. 那么arr是您想要的。

And if the result should be the btnCountViewsList : 如果结果应该是btnCountViewsList

btnCountViewsList.Clear();
btnCountViewsList.AddRange(arr);

So the lazy in me says, make a backfill list and use your existing (and gappy) list as a map. 因此,我这个懒惰的人说,创建一个回填列表,然后将您现有的列表(和大块头)用作地图。

public static IList<BtnCountViews> GetDefaultList()
{
    var defaultList = Enumerable.Range(1, 365).Select(e =>
        new BtnCountViews
        {
            DayOfYear = e,
            BtnCount = 0,
            Views = 0
        }
    ).ToList();

    return defaultList;
}

Iterate through the backfill list and consult the map to see if the DayOfYear value exists as a key, and if not, then add it to the map. 遍历回填列表并查阅地图,以查看DayOfYear值是否作为键存在,如果不存在,则将其添加到地图中。

public static IList<BtnCountViews> GetBackFilledList(IList<BtnCountViews> incoming)
{
    var map = incoming.ToDictionary(k => k.DayOfYear, v => v);
    var defaultList = GetDefaultList();

    foreach(var itm in defaultList)
    {
        if (map.ContainsKey(itm.DayOfYear)) continue;

        map.Add(itm.DayOfYear, itm);
    }

    return map.Select(m => m.Value).ToList();
}

Once the iteration is finished, convert the map into a list, which should now consist of the original values + default values for missing DayOfYear entries as well. 迭代完成后,将映射转换为列表,该列表现在还应包含原始值+缺少DayOfYear条目的默认值。

return map.Select(m => m.Value).ToList();

Dotnetfiddle of a sample program here: https://dotnetfiddle.net/wSJy56 这里是示例程序的Dotnetfiddle: https ://dotnetfiddle.net/wSJy56

Is there a more elegant way to do this? 有没有更优雅的方法可以做到这一点? Most surely. 最肯定的。 But this code executes in about 0.011 seconds, which to me is pretty decent so long as you're not calling this functionality over and over again (eg you decide to analyze 30 years of data and need to get that done in 0.011 seconds). 但是这段代码的执行时间约为0.011秒,对我来说,这很不错,只要您不一遍又一遍地调用此功能(例如,您决定分析30年的数据,而需要在0.011秒内完成)。 But then we'd have to be looking more towards parallelism rather than code elegance to solve that can of worms. 但是,我们必须更多地关注并行性,而不是优雅的代码来解决蠕虫问题。

Hope this helps... 希望这可以帮助...

Try the following 尝试以下

btnCountViewsList = btnCountViewsList.Where(b => b.BtnCount == 0).Where(v => v.Views == 0).ToList();

If I understood what you were asking, you want to get objects where BtnCount = 0 and Views = 0. 如果我理解您的要求,则想获取BtnCount = 0且Views = 0的对象。

This will select all the objects where Views = 0, and then that IEnumarable will be through another LINQ expression where it only selects the other property that equals to 0. 这将选择Views = 0的所有对象,然后该IEnumarable将通过另一个LINQ表达式,其中仅选择等于0的另一个属性。

The shortest linq way, using an left outer join ( LEFT OUTER JOIN in LINQ ) and Range 最短的linq方式,使用左外部联接(LINQ中的LEFT OUTER JOIN )和Range

 var result = (from a in Enumerable.Range(0, 365)
                         join lst in btnCountViewsList on a equals lst.DayOfYear into ps
                         from p in ps.DefaultIfEmpty()
                         select (p==null) ? new BtnCountViews() { DayOfYear = a}:p).ToList()

among the lines of some other responses, but without hard coding the total days of the year as leap years will have 366 days 在其他一些响应中,但如果不进行硬编码,则leap年的总天数为366天

var range = new 
{
    Start = new DateTime(2017, 1, 1),
    End = new DateTime(2017, 12, 31),
};

var days = Enumerable.Range(range.Start.DayOfYear, range.End.DayOfYear);

var query = from day in days
            from counter in 
            (
                from temp in btnCountViewsList
                where temp.DayOfYear == day
                select temp
            ).DefaultIfEmpty()
            select new BtnCountViews
            {
                DayOfYear = day,
                BtnCount = counter == null ? 0 : counter.BtnCount,
                Views = counter == null ? 0 : counter.Views,
            };

will give you something like 会给你像

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM