重构 linq 语句

发布于 2024-10-18 06:44:01 字数 2244 浏览 2 评论 0原文

我有一个在 LINQPad 中使用的 linq 表达式,我想重构该表达式,以用单个测试替换 idx == -1 的所有测试。此输入数据是对用于缓存 Active Directory 信息的数据库进行自由文本搜索的结果。搜索会返回匹配数据库行中的显示名称和关联摘要数据的列表。我想从该列表中提取显示名称和匹配的 Active Directory 条目。有时,匹配仅发生在显示名称上,因此可能没有进一步的上下文。在下面的示例中,字符串“Sausage”旨在作为返回 matches 数组中两项的搜索词。显然,实际搜索的情况并非如此,因为第二个数组项中没有匹配“Sausage”。

var matches = new [] 
{
    new { displayName = "Sausage Roll", summary = "|Title: Network Coordinator|Location: Best Avoided|Department: Coordination|Email: [email protected]|" },
    new { displayName = "Hamburger Pattie",  summary = "|Title: Network Development Engineer|Location: |Department: Planning|Email: [email protected]|" },
};

var context = (from match in matches
                let summary = match.summary
                let idx = summary.IndexOf("Sausage")
                let start = idx == -1 ? 0 : summary.LastIndexOf('|', idx) + 1
                let stop = idx == -1 ? 0 : summary.IndexOf('|', idx)
                let ctx = idx == -1 ? "" : string.Format("...{0}...", summary.Substring(start, stop - start))
                select new { displayName = match.displayName, summary = ctx, })
                .Dump();

我正在尝试创建一个名称列表和搜索结果的一些上下文(如果存在)。下面的输出表示 Dump() 显示的内容,并且是正确的结果:

displayName        summary 
----------------   ------------------------------------------
Sausage Roll       ...Email: [email protected]...
Hamburger Pattie

编辑:正则表达式版本如下,绝对更整洁:

Regex reg = new Regex(@"\|((?:[^|]*)Sausage[^|]*)\|");      
var context = (from match in matches
                let m = reg.Match(match.summary)
                let ctx = m.Success ? string.Format("...{0}...", m.Groups[1].Value) : ""
                select new { displayName = match.displayName, context = ctx, })
                .Dump();

I have a linq expression that I've been playing with in LINQPad and I would like to refactor the expression to replace all the tests for idx == -1 with a single test. The input data for this is the result of a free text search on a database used for caching Active Directory info. The search returns a list of display names and associated summary data from the matching database rows. I want to extract from that list the display name and the matching Active Directory entry. Sometimes the match will only occur on the display name so there may be no further context. In the example below, the string "Sausage" is intended to be the search term that returned the two items in the matches array. Clearly this wouldn't be the case for a real search because there is no match for Sausage in the second array item.

var matches = new [] 
{
    new { displayName = "Sausage Roll", summary = "|Title: Network Coordinator|Location: Best Avoided|Department: Coordination|Email: [email protected]|" },
    new { displayName = "Hamburger Pattie",  summary = "|Title: Network Development Engineer|Location: |Department: Planning|Email: [email protected]|" },
};

var context = (from match in matches
                let summary = match.summary
                let idx = summary.IndexOf("Sausage")
                let start = idx == -1 ? 0 : summary.LastIndexOf('|', idx) + 1
                let stop = idx == -1 ? 0 : summary.IndexOf('|', idx)
                let ctx = idx == -1 ? "" : string.Format("...{0}...", summary.Substring(start, stop - start))
                select new { displayName = match.displayName, summary = ctx, })
                .Dump();

I'm trying to create a list of names and some context for the search results if any exists. The output below is indicative of what Dump() displays and is the correct result:

displayName        summary 
----------------   ------------------------------------------
Sausage Roll       ...Email: [email protected]...
Hamburger Pattie

Edit: Regex version is below, definitely tidier:

Regex reg = new Regex(@"\|((?:[^|]*)Sausage[^|]*)\|");      
var context = (from match in matches
                let m = reg.Match(match.summary)
                let ctx = m.Success ? string.Format("...{0}...", m.Groups[1].Value) : ""
                select new { displayName = match.displayName, context = ctx, })
                .Dump();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暮凉 2024-10-25 06:44:01

(我知道这并不能回答你的具体问题),但无论如何,这是我的贡献:

你还没有真正描述你的数据是如何进来的。正如@Joe所建议的,你可以使用正则表达式或像我一样分割字段以下。

不管怎样,我建议重构你的代码以允许单元测试。

否则,如果您的数据无效/损坏,您将在 linq 查询中收到运行时错误。

    [TestMethod]
    public void TestMethod1()
    {
        var matches = new[] 
        {
            new { displayName = "Sausage Roll", summary = "|Title: Network Coordinator|Location: Best Avoided|Department: Coordination|Email: [email protected]|" },
            new { displayName = "Hamburger Pattie",  summary = "|Title: Network Development Engineer|Location: |Department: Planning|Email: [email protected]|" },
        };

        IList<Person> persons = new List<Person>();
        foreach (var m in matches)
        {
            string[] fields = m.summary.Split('|');
            persons.Add(new Person { displayName = m.displayName, Title = fields[1], Location = fields[2], Department = fields[3] });
        }

        Assert.AreEqual(2, persons.Count());
    }

    public class Person
    {
        public string displayName { get; set; }
        public string Title { get; set; }
        public string Location { get; set; }
        public string Department { get; set; }
        /* etc. */
    }

(I know this doesn't answer your specific question), but here's my contribution anyway:

You haven't really described how your data comes in. As @Joe suggested, you could use a regex or split the fields as I've done below.

Either way I would suggested refactoring your code to allow unit testing.

Otherwise if your data is invalid / corrupt whatever, you will get a runtime error in your linq query.

    [TestMethod]
    public void TestMethod1()
    {
        var matches = new[] 
        {
            new { displayName = "Sausage Roll", summary = "|Title: Network Coordinator|Location: Best Avoided|Department: Coordination|Email: [email protected]|" },
            new { displayName = "Hamburger Pattie",  summary = "|Title: Network Development Engineer|Location: |Department: Planning|Email: [email protected]|" },
        };

        IList<Person> persons = new List<Person>();
        foreach (var m in matches)
        {
            string[] fields = m.summary.Split('|');
            persons.Add(new Person { displayName = m.displayName, Title = fields[1], Location = fields[2], Department = fields[3] });
        }

        Assert.AreEqual(2, persons.Count());
    }

    public class Person
    {
        public string displayName { get; set; }
        public string Title { get; set; }
        public string Location { get; set; }
        public string Department { get; set; }
        /* etc. */
    }
£噩梦荏苒 2024-10-25 06:44:01

或者类似的东西:

    Regex reg = new Regex(@"^|Email.*|$");
    foreach (var match in matches)
    {
        System.Console.WriteLine(match.displayName + " ..." + reg.Match(match.summary) + "... ");
    }

我还没有测试过这个,可能甚至没有正确的语法,但只是为了让您了解如何使用正则表达式来做到这一点。

更新
好的,我已经看到你的答案,很高兴你发布它,因为我认为我没有解释清楚。
我希望你的答案最后看起来像这样(现在使用 LINQPad 进行测试,现在我明白你使用 LINQPad 的意思,因为它实际上运行 C# 程序而不仅仅是 linq 命令,太棒了!)无论如何,这就是它应该做的看起来像:

foreach (var match in matches)
    Console.WriteLine(string.Format("{0,-20}...{1}...", match.displayName, Regex.Match(match.summary, @"Email:(.*)[|]").Groups[1]));
}

就是这样,整个事情,完全去掉 linq!
我希望这能解决问题,你根本不需要 linq。

Or something like this:

    Regex reg = new Regex(@"^|Email.*|$");
    foreach (var match in matches)
    {
        System.Console.WriteLine(match.displayName + " ..." + reg.Match(match.summary) + "... ");
    }

I haven't tested this, probably not even correct syntax but just to give you an idea of how you could do it with regex.

Update
Ok, i've seen your answer and it's good that you posted it because I think i didn't explain it clearly.
I expected your answer to look something like this at the end (tested using LINQPad now, and now i understand what you mean by using LINQPad because it actually does run a C# program not just linq commands, awesome!) Anyway this is what it should look like:

foreach (var match in matches)
    Console.WriteLine(string.Format("{0,-20}...{1}...", match.displayName, Regex.Match(match.summary, @"Email:(.*)[|]").Groups[1]));
}

That's it, the whole thing, take linq out of it, completely!
I hope this clears it up, you do not need linq at all.

伤感在游骋 2024-10-25 06:44:01

像这样?

var context = (from match in matches
                let summary = match.summary
                let idx = summary.IndexOf("Sausage")
                let test=idx == -1 
                let start =test ? 0 : summary.LastIndexOf('|', idx) + 1
                let stop = test ? 0 : summary.IndexOf('|', idx)
                let ctx = test ? "" : string.Format("...{0}...", summary.Substring(start, stop - start))
                select new { displayName = match.displayName, summary = ctx, })
                .Dump();

like this?

var context = (from match in matches
                let summary = match.summary
                let idx = summary.IndexOf("Sausage")
                let test=idx == -1 
                let start =test ? 0 : summary.LastIndexOf('|', idx) + 1
                let stop = test ? 0 : summary.IndexOf('|', idx)
                let ctx = test ? "" : string.Format("...{0}...", summary.Substring(start, stop - start))
                select new { displayName = match.displayName, summary = ctx, })
                .Dump();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文