【问题标题】:Need help finding specific nodes in a incremented sequence?需要帮助查找递增序列中的特定节点?
【发布时间】:2018-03-07 15:58:22
【问题描述】:

我有一个字符串数组,格式如下(每个元素至少包含 3 个节点,名称为 xref,属性为 ref-typerid

<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref>, <xref ref-type="bibr" rid="ref104">104</xref>, <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref> <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>

我正在尝试遍历数组的每个元素并找到 3 个或更多节点 xref,它们各自的 rid 属性值增加了 +1,不包括文本 rid,并将它们输出到控制台或一个文件。

我已经完成了

foreach (var element in xrefs)
{
    XDocument xd = XDocument.Parse("<root>"+element+"</root>",LoadOptions.SetLineInfo);

    int len = xd.Descendants("xref").Count();

    var values = from El in xd.Descendants("xref").Take(len - 2)
        where El.CompareNext() && El.ElementsAfterSelf().FirstOrDefault().CompareNext()
        select El;
    foreach (var value in values)
    {

        Console.WriteLine(new string('-',50)+"\r\n"+element+"\r\n");
    }
}

其中xrefs 是数组,ElementsAfterSelf() 是一个方法,创建如下

static class T1
{

    public static Boolean CompareNext(this XElement xe)
    {
        return Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(xe.ElementsAfterSelf().FirstOrDefault().Attribute("rid").Value.Replace("ref", ""));
    }
}

现在它产生的结果是这样的

--------------------------------------------------
<xref ref-type="bibr" rid="ref20">[20]</xref> <xref ref-type="bibr" rid="ref21">[21]</xref> <xref ref-type="bibr" rid="ref22">[22]</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref2">[2]</xref>, <xref ref-type="bibr" rid="ref3">[3]</xref>, <xref ref-type="bibr" rid="ref4">[4]</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref4">[4]</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>


--------------------------------------------------
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>

下面的字符串写了两次,但我只想要一次,因为它是同一件事

<xref ref-type="bibr" rid="ref101">101</xref>, <xref ref-type="bibr" rid="ref102">102</xref>, <xref ref-type="bibr" rid="ref103">103</xref> <xref ref-type="bibr" rid="ref104">104</xref> <xref ref-type="bibr" rid="ref106">106</xref>
<xref ref-type="bibr" rid="ref11">[11]</xref>, <xref ref-type="bibr" rid="ref12">[12]</xref> <xref ref-type="bibr" rid="ref13">[13]</xref> <xref ref-type="bibr" rid="ref14">[14]</xref>

谁能帮忙?

这是我正在使用的sample xml filefull code

我试图在一些由 逗号 或 逗号和空格并将它们写入日志文件。我试图识别的连续节点应该将它们各自的属性rid 值增加+1 减去文本ref。除了refX 之外,任何其他具有不同rid 值的xref 节点都不需要检查。

【问题讨论】:

  • ref106 是怎么进来的?什么是“文字摆脱”? ElementsAfterSelfCompareNext?
  • 你想输出什么“他们”?你为什么在正文中使用foreach (value 而不是value
  • @NetMage CompareNext not ElementsAfterSelf my bad ...顺便检查更新的问题..

标签: c# linq linq-to-xml


【解决方案1】:

我认为 LINQ 版本不会显着改善代码或理解。

既然要输出元素之间的文本,可以写一个辅助函数输出两个节点之间的XNodes:

var dashes = new String('-', 50);

void WriteNodesBetween(XNode from, XNode to) {
    Console.WriteLine(dashes);
    var xn = from;
    for (; xn != to; xn = xn.NextNode)
        Console.Write(xn.ToString());
    Console.WriteLine(xn.ToString());
}

然后您可以将您的字符串转换为XNodes 并扫描收集按顺序编号的元素的元素。收集它们后,如果找到至少三个连续元素,则可以输出所有元素和节点之间的节点。

foreach (var element in xrefs) {
    var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");

    var outElements = new List<XElement>() { xd.First() };
    foreach (var el in xd.Skip(1)) {
        if (!outElements.Last().ISSequential(el)) {
            if (outElements.Count >= 3)
                WriteNodesBetween(outElements.First(), outElements.Last());
            outElements.Clear();
        }
        outElements.Add(el);
    }
    if (outElements.Count >= 3)
        WriteNodesBetween(outElements.First(), outElements.Last());
}

这使用另一个助手来测试两个元素是否是连续的。

public static class Ext {
    public static bool ISSequential(this XElement xe, XElement nextxe) => Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "")) + 1 == Convert.ToInt16(nextxe.Attribute("rid").Value.Replace("ref", ""));
}

【讨论】:

  • 您的代码仍然会产生与我相同的重复值...即&lt;xref ref-type="bibr" rid="ref101"&gt;101&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref102"&gt;102&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref103"&gt;103&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref104"&gt;104&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref106"&gt;106&lt;/xref&gt;&lt;xref ref-type="bibr" rid="ref11"&gt;[11]&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref12"&gt;[12]&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref13"&gt;[13]&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref14"&gt;[14]&lt;/xref&gt; 仍然被写入输出两次...这不应该发生...
  • 如果可能的话,应该只显示连续元素+1递增rid,即对于&lt;xref ref-type="bibr" rid="ref101"&gt;101&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref102"&gt;102&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref103"&gt;103&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref104"&gt;104&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref106"&gt;106&lt;/xref&gt;,输出应该显示&lt;xref ref-type="bibr" rid="ref101"&gt;101&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref102"&gt;102&lt;/xref&gt;, &lt;xref ref-type="bibr" rid="ref103"&gt;103&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref104"&gt;104&lt;/xref&gt;
  • as &lt;xref ref-type="bibr" rid="ref106"&gt;106&lt;/xref&gt;rid 值为 ref106 而不是 ref105,因为它应该是递增值
  • 我的输出只显示每个字符串一次。此外,您的程序输出element,这是整个原始字符串,例如将包括 ref106。您希望 ref101,ref102,ref103,ref106,ref107,ref108 的输出是什么?
  • ref101,ref102,ref103ref106,ref107,ref108 作为两个单独的字符串
【解决方案2】:

我把它作为一个单独的答案,因为我不确定我认为它更好或更清晰,但是可以使用 LINQ 通过对它们进行分组来查找序列。

我创建了 Scan 扩展的变体,它是 APL 扫描运算符的一种实现,类似于 Aggregate,但它以序列的形式返回中间结果。

ScanPair 扩展使用ValueTuple 将中间结果与结果序列中的当前元素进行匹配:

public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combine) {
    using (var srce = src.GetEnumerator()) {
        if (srce.MoveNext()) {
            var prevkv = (seedKey, srce.Current);

            while (srce.MoveNext()) {
                yield return prevkv;
                prevkv = (combine(prevkv, srce.Current), srce.Current);
            }
            yield return prevkv;
        }
    }
}

使用ScanPair,您可以创建一个扩展来对谓词进行分组:

public static IEnumerable<IGrouping<int, TRes>> GroupByWhile<T, TRes>(this IEnumerable<T> src, Func<T, T, bool> test, Func<T, TRes> result) =>
    src.ScanPair(1, (kvp, cur) => test(kvp.Value, cur) ? kvp.Key : kvp.Key+1)
       .GroupBy(kvp => kvp.Key, kvp => result(kvp.Value));
public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> test) => src.GroupByWhile(test, e => e);

使用GroupByWhile,您可以创建一个扩展来按顺序值分组:

public static IEnumerable<IGrouping<int, TRes>> GroupBySequential<T, TRes>(this IEnumerable<T> src, Func<T, int> SeqNum, Func<T, TRes> result) => src.GroupByWhile((prev,cur) => SeqNum(prev)+1 == SeqNum(cur), result);
public static IEnumerable<IGrouping<int, T>> GroupBySequential<T>(this IEnumerable<T> src, Func<T, int> SeqNum) => src.GroupBySequential(SeqNum, e => e);

现在GroupBySequential 可用,您可以从每个字符串中提取序列:

var dashes = new String('-', 50);

void WriteNodesBetween(XNode from, XNode to) {
    Console.WriteLine(dashes);
    var xn = from;
    for (; xn != to; xn = xn.NextNode)
        Console.Write(xn.ToString());
    Console.WriteLine(xn.ToString());
}

foreach (var element in xrefs) {
    var xd = XDocument.Parse("<root>" + element + "</root>").Descendants("xref");
    var refseqs = xd.GroupBySequential(xref => xref.RefValue().Value);
    foreach (var seq in refseqs.Where(sg => sg.Count() >= 3))
        WriteNodesBetween(seq.First(), seq.Last());
}

【讨论】:

  • 它看起来更复杂...但是感谢您展示这种替代方法...顺便说一句,如果文件包含节点,我最近注意到您之前的答案(我标记为答案)有一个小问题就像&lt;xref ref-type="bibr" rid="ref20"&gt;[20]&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref21"&gt;[21]&lt;/xref&gt; &lt;xref ref-type="bibr" rid="ref22"&gt;[22]&lt;/xref&gt; 一样,输出显示为&lt;xref ref-type="bibr" rid="ref20"&gt;[20]&lt;/xref&gt;&lt;xref ref-type="bibr" rid="ref21"&gt;[21]&lt;/xref&gt;&lt;xref ref-type="bibr" rid="ref22"&gt;[22]&lt;/xref&gt;,即两者之间没有空格,但我想要它,因为它完全在文件中..
  • 不幸的是,XDocument 等的重点是抽象出诸如间距之类的东西。这听起来像你需要一起采取不同的方法。您要解决的真正问题是什么?
【解决方案3】:

您的 xml 是一个元素数组,所以我不明白您要做什么。

<Root>
  <xref ref-type="bibr" rid="ref20">[20]</xref> 
  <xref ref-type="bibr" rid="ref21">[21]</xref> 
  <xref ref-type="bibr" rid="ref22">[22]</xref>
  <xref ref-type="bibr" rid="ref2">[2]</xref> 
  <xref ref-type="bibr" rid="ref3">[3]</xref> 
  <xref ref-type="bibr" rid="ref4">[4]</xref>
  <xref ref-type="bibr" rid="ref101">101</xref> 
  <xref ref-type="bibr" rid="ref102">102</xref> 
  <xref ref-type="bibr" rid="ref103">103</xref>
  <xref ref-type="bibr" rid="ref104">104</xref> 
  <xref ref-type="bibr" rid="ref106">106</xref>
  <xref ref-type="bibr" rid="ref11">[11]</xref> 
  <xref ref-type="bibr" rid="ref12">[12]</xref> 
  <xref ref-type="bibr" rid="ref13">[13]</xref> 
  <xref ref-type="bibr" rid="ref4">[4]</xref>
  <xref ref-type="bibr" rid="ref11">[11]</xref> 
  <xref ref-type="bibr" rid="ref12">[12]</xref> 
  <xref ref-type="bibr" rid="ref13">[13]</xref> 
  <xref ref-type="bibr" rid="ref14">[14]</xref>
</Root>

【讨论】:

  • 您的意思是“任何”==“一个”吗?
  • 此解决方案与我在以下帖子中对您之前的问题的回答有什么区别?我的 TestChildren() 方法可以轻松修改以处理这两种情况。 stackoverflow.com/questions/49095329/…
  • @jdweng 它的过程对我来说有点太长了,我希望在 values 变量中添加一个简单的单行 linq 条件或类似的东西来检查下一个 xref以及介于两者之间的字符串以及变量 values 本身中的 3 个或更多 xref,虽然简短但可以完成工作
  • 我的代码使用简单的 linq。辅助方法很复杂,但谁在乎。它完成了工作。为什么要创建一个没人能理解、没人能修改的单行 linq。在 60 年代,他们习惯将此称为功率编程。尝试减少使用非常昂贵内存的源代码的大小。当内存变得便宜时,编写可以维护的代码变得更加重要。员工经常更换工作,并且需要新员工来修改现有代码。因此,代码需要有良好的文档记录且易于更改。
猜你喜欢
  • 2014-08-22
  • 2011-05-31
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
相关资源
最近更新 更多