views:

255

answers:

6

Hi there,

I want to create a list out of an xml file where only values between two letters are displayed. For example, only show matching records where the first character of the title are in the range from "A" to "E".

My first basic approach was

where item.Element("title").Value >= "A" && item.Element("title").Value < "E"

Of course this didn't work as these operators can't be applied to strings. I tried using string.compare but without luck.

Can anyone give me an example on how to achieve this query?

cheers, Terry

Update: Below is the code that for reading the xml.

var simpleListSource = (from item in doc.Descendants("item")
    where item.Element("title").Value[0] >= 'A' && item.Element("title").Value[0] < 'E'
    orderby item.Element("title").Value
      select new
          {
            title = item.Element("title").Value,
            description = item.Element("description").Value,
            image = item.Element("image").Value,
            type = item.Element("type").Value,
            imagelink = item.Element("imagelink").Value

          }).ToList();

    rptGetItems.DataSource = simpleListSource;
    rptGetItems.DataBind();

And the xml...

  <item>
        <title>A test</title>
        <titlelong>A</titlelong>
        <type>test</type>
        <description>
     Some description

        </description>
        <image>includes/images/test.jpg</image>
        <imagelink>http://somesite.com&lt;/imagelink&gt;

    </item>
    <item>
        <title>E test2</title>
        <titlelong>E</titlelong>
        <type>test</type>
        <description>
     Another sample

        </description>
        <image>includes/images/test.jpg</image>
        <imagelink>http://somesite.com&lt;/imagelink&gt;

    </item>
+1  A: 
where Enumerable.Range('A','E').Select(x => (char) x).Contains(title[0])

(untested)

leppie
+4  A: 

Use chars and compare them to the first char in the title:

where item.Element("title").Value[0] >= 'A' 
      && item.Element("title").Value[0] < 'E'

You might want to test for Empty titles...

    bool TitleInRange(string title)
    {
        if (title == null || title.Trim() == string.Empty)
            return false;

        return (title[0] >= 'A' && title[0] <= 'E')
            || (title[0] >= 'a' && title[0] <= 'e');
    }

    ...
    where TitleInRange(item.Element("title").Value)

(update)

Your code works as expected. Just change the < E to <= E. Try printing the titles to console:

        foreach (var item in simpleListSource)
        {
            Console.WriteLine("{0}", item.title);
        }

Your problem must be related to data binding.

bruno conde
Hi Bruno, tried both of your approaches, it didn't work. No results are displayed. Tried to change the entries in the xml to only one character, same result. Any hints?
Terry Marder
@Terry, the problem might not be related to this. Can you show us more code? Just a simple snippet of what your doing.
bruno conde
Hi Bruno, I updated it with the source... thanks...
Terry Marder
@Terry see my updated answer. Your problem must be in data binding.
bruno conde
Thanks Bruno, thanks for your help. I found the error, empty title xml node... yeah I know, stupid, stupid...
Terry Marder
A: 

Try:

where item.Element("title").Value[0] >= 'A' && item.Element("title").Value[0] < 'E'

Now you're comparing char to char, which can use normal comparison operators.

EDIT: here is an example programme:

using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;

namespace TestIdeas
{
    class Program
    {
        static void Main(string[] args)
        {
            XElement i1 = XElement.Parse(@" <item>
                                                <title>A test</title>
                                                <titlelong>A</titlelong>
                                                <type>test</type>
                                                <description>
                                                    Some description
                                                </description>
                                                <image>includes/images/test.jpg</image>
                                                <imagelink>http://somesite.com&lt;/imagelink&gt;

                                            </item>");

            XElement i2 = XElement.Parse(@" <item>
                                                <title>E test2</title>
                                                <titlelong>E</titlelong>
                                                <type>test</type>
                                                <description>
                                                    Another sample
                                                </description>
                                                <image>includes/images/test.jpg</image>
                                                <imagelink>http://somesite.com&lt;/imagelink&gt;
                                            </item>");

            XElement root = new XElement("root");
            root.Add(new[] { i1, i2 });

            var ts = from t in root.Elements("item").Elements("title")
                      where t.Value[0] >= 'A' && t.Value[0] <= 'E'
                      select t;

            foreach (XElement t in ts)
            {
                Console.WriteLine(t.Value);
            }

            Console.ReadLine();
        }
    }
}

Gives the output

A Test
E Test2
Matt Ellen
Hi Matt, tried your approach, unfortunately it didn't work. No results are displayed. Any hints?
Terry Marder
Hi Terry,I tested against the XML you provided and I got A but not E (as expected). If you want to include E, use <= 'E'
Matt Ellen
Hi Matt, thanks for your help I found the error, emtpty title xml node... yeah I know, stupid, stupid...
Terry Marder
No worries! Happy to help.
Matt Ellen
+2  A: 
where "ABCDE".Contains(item.Element("title").Value[0])
LukeH
A: 

what about a regex

 Regex searchTerm = new Regex("^[a-eA-E]");

 var simpleListSource = (from item in doc.Descendants("item")
where searchTerm.Matches(item.Element("title").Value).Count > 0
orderby item.Element("title").Value
  select new
      {
        title = item.Element("title").Value,
        description = item.Element("description").Value,
        image = item.Element("image").Value,
        type = item.Element("type").Value,
        imagelink = item.Element("imagelink").Value

      }).ToList();

rptGetItems.DataSource = simpleListSource;
rptGetItems.DataBind();

not tested but should be close (the regex may need a little work)

Pharabus
just noticed yours is all uppercase so the regex may need to be ^[A-E]
Pharabus
A: 

For that I think I would maybe make a method like this:

public static bool IsInRange<T>(T subject, T first, T last)
{
    return IsInRange(subject, first, last, Comparer<T>.Default);
}

public static bool IsInRange<T>(T subject, T first, T last, IComparer<T> comparer)
{
    return comparer.Compare(subject, first) >= 0 && comparer.Compare(subject, last) <= 0;
}

Or something similar. Not tested, but think it should work :P

Update: To use it in your example, you could do something like this:

var items = doc.Descendants("item")
        .Where(x => IsInRange(x.Element("title").Value[0], "A", "E")
        .OrderBy(x => x.Element("title").Value
        .Select(x => new
            {
                title = x.Element("title").Value,
                description = x.Element("description").Value,
                image = x.Element("image").Value,
                type = x.Element("type").Value,
                imagelink = x.Element("imagelink").Value
            })
        .ToList();
Svish