Reputation: 1753
I got this XML file:
<Msg UserText="start 0">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 1">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 2">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
I need to count How many 'A's are between each "start x"
i.e. for the above i would output:
start 0 : 2
start 1 : 1
start 2 : 3
How should i go about this in C# ? i had a few directions but I'm sure there are easier ones out there (e.g. using linq)
Upvotes: 0
Views: 260
Reputation: 167571
Here is a grouping sample:
XDocument doc = XDocument.Load("../../XMLFile1.xml");
var groups = from msg in doc.Root.Elements("Msg")
where !((string)msg.Attribute("UserText")).StartsWith("start")
group msg by
msg.ElementsBeforeSelf("Msg").Where(m =>
((string)m.Attribute("UserText")).StartsWith("start")).Last();
foreach (var group in groups)
{
Console.WriteLine("Group starting with {0} has {1} member(s).",
group.Key.Attribute("UserText"), group.Count());
}
With the XML input sample XMLFile1.xml being
<Root>
<Msg UserText="start 0">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 1">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 2">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
</Root>
I get the output
Group starting with UserText="start 0" has 2 member(s).
Group starting with UserText="start 1" has 1 member(s).
Group starting with UserText="start 2" has 3 member(s).
Upvotes: 2
Reputation: 134861
Take advantage of the ElementsAfterSelf()
method to get the following siblings. Then it's pretty easy from there:
var root = XElement.Parse(xmlStr);
var query =
from msg in root.Elements("Msg")
let message = (string)msg.Attribute("UserText")
where message.StartsWith("start")
select new
{
Message = message,
FollowingAs = msg.ElementsAfterSelf("Msg")
.TakeWhile(e => (string)e.Attribute("UserText") == "A")
.Count(),
};
Upvotes: 1
Reputation: 7931
static void Main(string[] args)
{
const string xml = @"<SomeRootTag>
<Msg UserText='start 0'>
</Msg>
<Msg UserText='A'>
</Msg>
<Msg UserText='A'>
</Msg>
<Msg UserText='start 1'>
</Msg>
<Msg UserText='A'>
</Msg>
<Msg UserText='start 2'>
</Msg>
<Msg UserText='A'>
</Msg>
<Msg UserText='A'>
</Msg>
<Msg UserText='A'>
</Msg>
</SomeRootTag>";
var xDoc = XDocument.Load(new StringReader(xml));
var msgs = xDoc.Root.Elements().Where(el => el.Name == "Msg").Select(el => el.Attribute("UserText").Value);
var results = GetCounts(msgs);
foreach (var keyValue in results)
{
Console.WriteLine("{0}:{1}", keyValue.Item1, keyValue.Item2);
}
Console.ReadKey();
}
private static IEnumerable<Tuple<string,int>> GetCounts(IEnumerable<string> msgs)
{
string last = null;
int count = 0;
foreach (var msg in msgs)
{
if (msg.StartsWith("start"))
{
if (last != null)
{
yield return new Tuple<string, int>(last, count);
}
count = 0;
last = msg;
}
else
{
count++;
}
}
yield return new Tuple<string, int>(last, count);
}
Upvotes: 1
Reputation: 9660
This should do it for you.
using System.Collections.Generic;
using System.Xml.Linq;
namespace TraverseXMLNodes
{
public class Class1
{
public static void Main(string[] args)
{
XDocument doc = XDocument.Load(@"C:\Udvikling\StackOverflow\TraverseXMLNodesSln\TraverseXMLNodes\XMLFile1.xml");
var msgs = doc.Element("root").Elements("Msg");
List<int> numbers = new List<int>();
List<string> numbersStr = new List<string>();
int count = 0;
foreach (var xElement in msgs)
{
string value = xElement.Attribute("UserText").Value;
numbersStr.Add(value);
if (value.Contains("start"))
{
numbers.Add(count);
count = 0;
}
else
{
count++;
}
}
numbers.Add(count);
}
}
}
My XML file:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Msg UserText="start 0">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 1">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 2">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
</root>
Upvotes: 1