代码之家  ›  专栏  ›  技术社区  ›  halivingston

Facebook采访问题:格式化电影放映时间输出的时间集合(首选使用LINQ)

  •  4
  • halivingston  · 技术社区  · 14 年前
    class TimeObject
    {
        DateTime time;
        bool isMatinee;
    }
    
    Given: {8:00, 9:30, 11:00, 12:10, 2:00, 4:00, 5:20} -- a collection of TimeObjects
    
    Output: (8:00AM, 9:30, 11:00, 12:10PM, 2:00), 4:00, 5:20 -- return a string, oh and AM/PM should be picked up from localization strings
    
    Caveats: AM/PM only shown for first time, ( ) encloses those elements whose matinee bool was set to true.
    

    问题是:我必须弄清楚如何输出上面的字符串。

    我提到过,我知道C,面试官坚定不移地知道如何在最少的可读代码行中做到这一点,最好是使用LINQ。他说,我可以把它写在控制台上,但我必须记住把AM和PM本地化。

    很明显,我创建了一堆临时收集和垃圾,完全把它弄糟了。他声称这只是几行直线。我试过其他的方法,尽管他一直在指引我走向林肯。

    帮助?有人有想法吗?这一天我真的很害怕。

    更新 -我找到工作了!他们还要求我编辑距离hello-->hlo,告诉最小编辑/更新次数,以获得最终字符串。有蜜蜂,世界上有蜂蜜,有1只蜂王,只有通过蜂王才能获得蜂蜜。构建一个可以支持这一点的“制造信仰”计算机世界,并判断世界是否违反了这一点。-- ,根节点是蜂王,节点是蜂蜜和蜜蜂,运行二部分测试看世界是否违反。

    4 回复  |  直到 14 年前
        1
  •  2
  •   Jeff Mercado    14 年前

    [编辑]
    我相信有一个更清洁的方法来做这件事,但这就是我所拥有的。如果没有时间格式限制,简单的分组会更容易。我会设法想出另一个版本。

    var amTimes = times.Where(to => to.time.Hour < 12)
                       .Select((to, i) => new
                       {
                           to.isMatinee,
                           repr = i == 0 ? to.time.ToString("h:mmtt")
                                         : to.time.ToString("h:mm")
                       });
    var pmTimes = times.Where(to => to.time.Hour >= 12)
                       .Select((to, i) => new
                       {
                           to.isMatinee,
                           repr = i == 0 ? to.time.ToString("h:mmtt")
                                         : to.time.ToString("h:mm")
                       });
    var stimes = amTimes.Concat(pmTimes);
    var mats = String.Join(", ", stimes.Where(t => t.isMatinee).Select(t => t.repr));
    var nonmats = String.Join(", ", stimes.Where(t => !t.isMatinee).Select(t => t.repr));
    
    var output = string.Format("({0}), {1}", mats, nonmats);
    

    [编辑2]
    好吧,这很可能是面试官想要的答案。

    var output = String.Join(", ",
        times.Select(to => new
        {
            prefix = to == times.First(t => t.isMatinee) ? "(" : "",
            time = to.time,
            fmt = to.time.Hour < 12
                ? (to == times.First(t => t.time.Hour < 12) ? "h:mmtt" : "h:mm")
                : (to == times.First(t => t.time.Hour >= 12) ? "h:mmtt" : "h:mm"),
            suffix = to == times.Last(t => t.isMatinee) ? ")" : "",
        })
        .Select(x => String.Format("{0}{1}{2}", x.prefix, x.time.ToString(x.fmt), x.suffix)));
    

    通常,当我编写LINQ表达式时,我总是考虑性能。因为在这里性能不是一个因素。这应该是最容易编写和跟踪的(但性能糟糕)。

    该方法考虑时间、格式化方式以及打印时的任何前缀(开放paren)或后缀(结束paren)。只要 isMatinee 分组是连续的(我一直假设这一点),按时间排序,这应该总是有效的。

    它只有在第一次是日场时才有前缀。如果是最后一次日场,则为后缀。如果是其各自组中的第一次,则应使用AM/PM格式化。这应该很容易理解。


    如果只涉及日场分组,我可能会这样做:

    var output = String.Join(", ",
        times.GroupBy(to => to.isMatinee, to => to.time.ToString("h:mm"))
             .Select(g => g.Key ? "(" + String.Join(", ", g) + ")"
                                : String.Join(", ", g)));
    
        2
  •  1
  •   Francisco    14 年前
    bool AMShown = false;
    bool PMShown = false;
    StringBuilder sb = new StringBuilder();
    
    //assuming it groups with false first:
    foreach(var timeObjGrp in collection.GroupBy(p=>p.isMatinee))
    {
        if (grpTimeObj.Key) StringBuilder.Append("(");
        foreach (var timeObjItem in timeObjGrp)
        {
            StringBuilder.Append(timeObjItem.time.ToString("h:m"));
            //IsAM should be something like Hours < 12
            if (!AMShown && timeObjItem.time.IsAM)
            {
                StringBuilder.Append("AM");
                AMShown = true;
            }
            if (!PMShown && timeObjItem.time.IsPM)
            {
                StringBuilder.Append("PM");
                PMShown = true;
            }
            StringBuilder.Append(",");
        }
        //here put something to remove last comma
        if (grpTimeObj.Key) StringBuilder.Append(")");
        StringBuilder.Append(",");
    }
    //here put something to remove last comma
    

    我不确定它是否足够好读

        3
  •  0
  •   Konstantin Oznobihin    14 年前

    我很难把它称为简短的(和可读的),但它几乎都是linq,至少:

    
                var outputGroups =
                    from item in given
                    let needSuffix =
                        object.ReferenceEquals(item, given.FirstOrDefault(to => to.Time.Hour < 12)) ||
                        object.ReferenceEquals(item, given.FirstOrDefault(to => to.Time.Hour >= 12))
                    let suffix = needSuffix ? "tt" : string.Empty // could use AM/PM from resources instead of DateTime format string
                    let timeFormat = string.Format("H:mm{0}", suffix)
                    group item.Time.ToString(timeFormat) by item.IsMatinee into matineeGroup
                    orderby matineeGroup.Key descending
                    let items = string.Join(", ", matineeGroup.ToArray())
                    let format = matineeGroup.Key ? "({0})" : "{0}"
                    select string.Format(format, items);
    
                var result = string.Join(", ", outputGroups.ToArray());
    
    
        4
  •  0
  •   Enigmativity    14 年前

    这个怎么样?

    var output =
        String.Join(", ",
            from g in given
            orderby g.time
            group g by g.time.Hour < 12 into ggs
            select
                String.Join(", ", ggs.Select((x, n) =>
                {
                    var template = n == 0 ? "{0:h:mmtt}" : "{0:h:mm}";
                    template = x.isMatinee ? String.Format("({0})", template) : template;
                    return String.Format(template, x.time);
                }).ToArray())
        ).Replace("), (", ", ");
    

    (这里的假设是时间只适用于一天。)


    哈利文斯顿

    要回答您的问题:

    Q1。查询的工作方式。

    • 外部 String.Join 如您所说,用于将所有单独格式化的时间连接到一个字符串中。

    • 由于linq在本质上是功能性的,所以可以更容易地独立于其他元素计算每个元素,因此我将每个日场时间划分为两个时间段。这在格式化时间周围引入了冗余的括号,这些时间被 Replace 功能。

    • LINQ查询命令 TimeObject 实例通过 time (因为我们不知道在获取集合时它们是否必须是有序的),然后它用一个布尔值对它们进行分组,其中 true 是“AM” false 是“PM”。变量 ggs 是创建的组,按我的惯例命名为“G组”。

    • 每个组包含一个 IEnumerable<TimeObject> 它需要迭代来构建每个格式化的字符串。但是,我们需要区分第一个元素和其余元素,因此我们需要使用 Select 提供元素和元素索引的重载。一旦我们拥有了,我们就可以建立一个 String.Format 将正确添加AM/PM到第一个元素并将括号添加到的模板 每个 日场元素。

    • 连接字符串 再次用于将单个元素连接到单个字符串中。结果是Linq查询返回 IEnumerable<string> 有两个元素。(外部) 连接字符串 将这些转换为最后一个字符串。)

    Q2。为了允许时间跨越多天,我将进行以下更改:

    group g by g.time.Date.AddHours(g.time.Hour < 12 ? 0.0 : 12:00) into ggs
    

    这将允许在仍然分隔AM和PM的情况下将任意天数分组在一起。

    我希望这有帮助。