views:

152

answers:

4

The code below works but I want to optimize the code using yield or by changing algorithm.

public IEnumerable<Book> GetAuthorWithBookName(string keyword)
{
    var bookAndAuthorList = new List<Book>();
    List<Author> AuthorNameList = getAuthorName(keyword);
    foreach (var author in AuthorNameList)
    {
        XmlNode booksNames = getBook(author);
        XDocument XDOCbooksNames = XDocument.Parse(booksNames.OuterXml);

        var bookNameList = (
            from x1 in XDOCbooksNames.Descendants("Books")
            select x1.Elements("book").Select(g => g.Attribute("name").Value))
            .ToList();
        foreach (var bookName in bookNameList)
        {
            bookAndAuthorList.Add(new Book()
            {
                authorName = author.authorName,
                bookName = bookName
            });
        }
    }
    return bookAndAuthorList;
}

public class Book
{
    public string authorName { get; set; }
    public string bookName { get; set; }
}
A: 

Try this:

foreach (var bookName in bookNameList)
{
    yield return new Book()
    {
        authorName = author.authorName,
        bookName = bookName
    };
}

And remove other return statement.

Rubens Farias
+1  A: 

Not sure you'll get much 'optimization'. Yield is better used when you would often break part way through using the enumeration you are generating, so that you don't do unnecessary computation. But here goes:

This is untested.

public IEnumerable<Book> GetAuthorWithBookName(string keyword)
{
    foreach (var author in getAuthorName(keyword))
    {
        XDocument XDOCbooksNames = XDocument.Parse(getBook(author).OuterXml);

        var bookNameList = from x1 in XDOCbooksNames.Descendants("Books")
                            select x1.Elements("book").Select(g => g.Attribute("name").Value);

        foreach (var bookName in bookNameList)
        {
            yield return new Book()
                {
                    authorName = author.authorName,
                    bookName = bookName
                };
        }
    }
}

What I did:

  1. Remove the ToList() on the expression, it already returns an enumerable. removed some code by consolidating getAuthorName into the foreach (note - make sure that function yeilds and ienumerable too, if you can)
  2. Yeild return instead of adding to a list
Luke Schafer
+6  A: 

As a quick win, you can remove the .ToList() call. All you're doing is enumerate items, so there's no need to do that. Similarly, there's no need to create bookAndAutherList.

Eventually I think you can strip it down to this:

public IEnumerable<Book> GetAuthorWithBookName(string keyword)
{
    return from author in getAuthorName(keyword)
           let book = getBook(author)
           from xmlBook in XDocument.Parse(book.OuterXml).Descendants("Books")
           select new Book
           {
               authorName = author.AuthorName,
               bookName = xmlBook.Attribute("name").Value
           };
}
Sander Rijken
This is the best response. +1. @NETQuestion: LINQ automatically builds IEnumerable queries that implement lazy iteration (i.e. use the yield keyword during an iteration). As long as you can keep your data source in memory, and your code certainly implies that you do, just return the LINQ query itself and you're golden.
Randolpho
Also, I wanted to mention that let is surprisingly underused. Another +1 to a random answer just for mentioning it! :)
Randolpho
+1 for "let" - I just thought "Wait, is that F# or something?" - LINQ is really a language in itself.
Michael Stum
+9  A: 

Responses by Rubens and Luke correctly explain the use of yield.

However, this looks suspicious to me.

XmlNode booksNames = getBook(author);
XDocument XDOCbooksNames = XDocument.Parse(booksNames.OuterXml);

You convert XML to string and then parse it again, only because you want to convert it from DOM Node to Xml.Linq node. If you are talking about optimization, then this is much more inefficient than creating an extra list.

yu_sha
Good point, if I had more upvotes I would +1!
Sander Rijken
Looks like the best solution to that is to have an implementation of getBook that returns an `XElement` or `XDocument`
Sander Rijken
+1, nice catch!
Rubens Farias
@yu_sha getBook(author) is a third party web service which returns XmlNode.I can write a code to get the bookname from XmlNode but I think its easy with LINQ.
NETQuestion
It may be easy to code with LINQ, but converting XML tree to string and back is inefficient and feels dirty. There's no point optimizing anything else in this function if you do that. The only reason for doing it is using LINQ. But LINQ is just convenient syntactic sugar for extracting nodes. XmlElement has quite adequate interface for doing this.
yu_sha