C# 需要帮助查找递增序列中的特定节点吗?

C# 需要帮助查找递增序列中的特定节点吗?,c#,linq,linq-to-xml,C#,Linq,Linq To Xml,我用下面的方式得到了一个字符串数组(每个元素至少包含3个节点,名称为xref,属性为ref-type和rid) 其中,xrefs是数组,ElementsAfterSelf()是如下创建的方法 static class T1 { public static Boolean CompareNext(this XElement xe) { return Convert.ToInt16(xe.Attribute("rid").Value.Replace("ref", "

我用下面的方式得到了一个字符串数组(每个元素至少包含3个节点,名称为
xref
,属性为
ref-type
rid

其中,
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>
--------------------------------------------------
[20] [21] [22]
--------------------------------------------------
[2], [3], [4]
--------------------------------------------------
101, 102, 103 104 106
--------------------------------------------------
101, 102, 103 104 106
--------------------------------------------------
[11], [12] [13] [4]
--------------------------------------------------
[11], [12] [13] [14]
--------------------------------------------------
[11], [12] [13] [14]
它是写下面的字符串两次,但我只想要一次,因为它是相同的东西

<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>
101102103104106
[11], [12] [13] [14]
有人能帮忙吗

这是我正在使用的


我试图在一些xml文件中找到一些连续的节点,
(当有3或更多节点时),这些节点由逗号或逗号和空格分隔,并将它们写入日志文件。我试图识别的连续节点的相应属性
rid
值应增加+1减去文本
ref
。除了
refX
之外,不需要检查具有不同
rid
值的任何其他
xref
节点

您的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>

[20] 
[21] 
[22]
[2] 
[3] 
[4]
101
102
103
104
106
[11] 
[12] 
[13] 
[4]
[11] 
[12] 
[13] 
[14]

我认为LINQ版本不能显著提高代码或理解能力

由于您希望输出元素之间的文本,因此可以编写一个helper函数来输出两个节点之间的
XNode
s:

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());
}
然后,您可以将字符串转换为
XNode
s,并扫描收集顺序编号元素的元素。收集完这些元素后,如果至少找到三个连续元素,则可以输出所有元素以及它们之间的节点

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());
}

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

我创建了我的
Scan
扩展的一个变体,它是APL Scan操作符的一个实现,类似于
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;
        }
    }
}
使用
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);

你的意思是“任何”=“一个”?这个解决方案和我在下面的帖子中对你上一个问题的回答有什么区别?我的TestChildren()方法可以很容易地修改以处理这两种情况@jdweng对于我来说,这是一个有点太长的过程,我希望在
变量中添加一个简单的单行linq条件,或者类似的条件,检查下一个
外部参照
和中间的字符串,并在变量
本身中获得3个或更多的
外部参照
,一些简短但却能完成任务的代码使用简单的linq。helper方法很复杂,但谁在乎呢。它完成了任务。为什么要创建一个没有人可以理解和修改的单行linq呢。在60年代,他们称之为电源编程。尽量减少使用非常昂贵内存的源代码的大小。当内存变得便宜时,更重要的是编写可以维护的代码。员工经常更换工作,需要新员工修改现有代码。因此,代码需要有很好的文档记录,并且易于更改。
ref106
是如何进入其中的?什么是“文本”
ElementsAfterSelf
CompareNext
?要输出什么“它们”?你为什么要对每个(值)进行
foreach(value
而不在身体中使用
value
?@NetMage
CompareNext
ElementsAfterSelf
我的不好…顺便检查一下更新后的问题..你的代码仍然会产生与我相同的值…即
101、102、103、104、106
[11]、[12][13][14]
仍会写入输出两次…这不应该发生…如果可能,也只应显示+1递增的
rid
连续元素,即对于
101、102、103 104 106
而言,输出应显示
101、102、103 104
,因为
106
rid
值为ref106而非ref105对于递增的值,我的输出仅显示每个字符串一次。此外,您的程序输出
元素
,这是整个原始字符串,例如将包括ref106。您希望从ref101、ref102、ref103、ref106、ref107、ref108得到什么输出?
ref101、ref102、ref103
ref106、ref107、ref108
由于两个单独的字符串看起来更复杂…但感谢您展示了此替代方法…顺便说一句,您以前的答案(我标记为答案)有一个我最近注意到的小问题,如果文件包含像
[20][21][22]
这样的节点,则输出显示为
[20][21][22]
也就是说,中间没有空格,但我想要的是文件中的空格。不幸的是,
XDocument
等的目的是将空格之类的东西抽象出来。听起来你需要采取不同的方法。你真正想解决的问题是什么?
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", ""));
}
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;
        }
    }
}
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);
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);
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());
}