Selecting From a Class

Introduction

We know how to query an array of primitive values such as numbers (integers or decimal). The values used in a LINQ statement can also come from an interface, a class, a record, or a structure. This means that, in some LINQ scenarios, instead of using one of the primitive types to create a list, you can use an interface, a class, a record, or a structure. You can start by creating the interface, the class, the record, or the structure. Here is an example:

readonly record struct Video
{
    internal string? Title    { get; init; }
    internal int YearReleased { get; init; }
}

Of course, you can create all appropriate types of members in the body of the interface class, record, or structure. You primarily use the interface, the class, the record, or the structure as you would any other. In your LINQ statement, you can refer to all members of the array:

var vdos = from videos
           in aryVideos
           select videos;

In this case, the value of the select statement represents the whole variable, that is, all members of the array. If you want to get a property (or any member) of the class, apply the period operator to the value of select and access the desired member. Here is an example:

namespace VideoCollection
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Video[] aryVideos = new Video[]
            {
                new Video(792075, "Two for the Money",             "R",     2008, true),
                new Video(900245, "Her Alibi",                     "PG-13", 1998, false),
                new Video(773022, "Distinguished Gentleman (The)", "R",     1992, false),
                new Video(180358, "Memoirs of a Geisha",           "PG-13", 2006, true),
                new Video(961973, "Wall Street",                   "R",     2000, false)
            };

            var vdos = from videos
                       in aryVideos
                       select videos;

            foreach (var item in vdos)
                lbxVideos.Items.Add(item.Title!);
        }
    }

    internal readonly record struct Video
    {
        internal int     ShelfNumber  { get; init; }
        internal string? Title        { get; init; }
        internal string? Rating       { get; init; }
        internal int     YearReleased { get; init; }
        internal bool    WideScreen   { get; init; }

        public Video(int id, string title, string rating, int year, bool hd)
        {
            ShelfNumber  = id;
            Title        = title;
            Rating       = rating;
            YearReleased = year;
            WideScreen   = hd;
        }
    }
}

This would produce:

Video Collection

This technique allows you to access only one member of the class.

As mentioned already, the select statement primarily produces the whole array of the values of the variable. Since this value represents an array, you can use it in a list-based scenario, such as displaying the result in a list view. In this case, to access a member of the class, use a for or foreach loop to get each item of the array variable and apply the period operator on that value. Here is an example:

namespace VideoCollection
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Video[] aryVideos = new Video[]
            {
                new Video(792075, "Two for the Money",             "R",     2008, true),
                new Video(900245, "Her Alibi",                     "PG-13", 1998, false),
                new Video(773022, "Distinguished Gentleman (The)", "R",     1992, false),
                new Video(180358, "Memoirs of a Geisha",           "PG-13", 2006, true),
                new Video(961973, "Wall Street",                   "R",     2000, false)
            };

            var vdos = from videos
                       in aryVideos
                       select videos;

            foreach (var vdo in vdos)
            {
                ListViewItem lviVideo = new ListViewItem(vdo.ShelfNumber.ToString());

                lviVideo.SubItems.Add(vdo.Title);
                lviVideo.SubItems.Add(vdo.Rating);
                lviVideo.SubItems.Add(vdo.YearReleased.ToString());
                lviVideo.SubItems.Add(vdo.WideScreen.ToString());
                lvwVideos.Items.Add(lviVideo);
            }
        }
    }

    internal readonly record struct Video
    {
        internal int     ShelfNumber  { get; init; }
        internal string? Title        { get; init; }
        internal string? Rating       { get; init; }
        internal int     YearReleased { get; init; }
        internal bool    WideScreen   { get; init; }

        public Video(int id, string title, string rating, int year, bool hd)
        {
            ShelfNumber  = id;
            Title        = title;
            Rating       = rating;
            YearReleased = year;
            WideScreen   = hd;
        }
    }
}

This would produce:

Video Collection

You can also create a query that performs an operation on the members of the class. Here is an example that applies the + operator on string-based properties of a class:

namespace HumanResources
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Contractor[] contractors = new Contractor[]
            {
                new Contractor() { Code = 971974, FirstName = "Patricia",  LastName = "Katts",     HourlySalary = 24.68M },
                new Contractor() { Code = 208411, FirstName = "Raymond",   LastName = "Kouma",     HourlySalary = 20.15M },
                new Contractor() { Code = 279374, FirstName = "Jeannette", LastName = "Mukoko",    HourlySalary = 15.55M },
                new Contractor() { Code = 707912, FirstName = "Bertrand",  LastName = "Yamaguchi", HourlySalary = 24.68M },
                new Contractor() { Code = 971394, FirstName = "Gertrude",  LastName = "Monay",     HourlySalary = 20.55M }
            };

            var empls = from staffMembers
                        in contractors
                        select staffMembers.LastName + ", " + staffMembers.FirstName;

            foreach (var empl in empls)
                lbxContractors.Items.Add(empl);
        }
    }
    readonly internal record struct Contractor
    {
        internal int     Code         { get; init; }
        internal string? FirstName    { get; init; }
        internal string? LastName     { get; init; }
        internal decimal HourlySalary { get; init; }
    }
}

This would produce:

Employees

Calling a Method

To perform a type of operation on a class, you can create a method in the class and then call that method in your LINQ statement. This means that, just as you can access a field or a property of a class, you can access any of its internal or public methods. You can then call the method. Here is an example of claing a method in a LINQ statement:

namespace VideoCollection
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Video[] lstVideos = new Video[]
            {
                new Video(792075, "Two for the Money",             "R",     2008, true),
                new Video(900245, "Her Alibi",                     "PG-13", 1998, false),
                new Video(773022, "Distinguished Gentleman (The)", "R",     1992, false),
                new Video(180358, "Memoirs of a Geisha",           "PG-13", 2006, true),
                new Video(961973, "Wall Street",                   "R",     2000, false)
            };

            var vdos = from videos
                       in lstVideos
                       select videos.GetVideo();

            foreach (var vdo in vdos)
                cbxVideos.Items.Add(vdo);
        }
    }

    internal readonly record struct Video
    {
        internal int     ShelfNumber  { get; init; }
        internal string? Title        { get; init; }
        internal string? Rating       { get; init; }
        internal int     YearReleased { get; init; }
        internal bool    WideScreen   { get; init; }

        public Video(int id, string title, string rating, int year, bool hd)
        {
            ShelfNumber  = id;
            Title        = title;
            Rating       = rating;
            YearReleased = year;
            WideScreen   = hd;
        }

        internal string GetVideo()
        {
            return Title + ", " + YearReleased.ToString();
        }
    }
}

This would produce:

Video Collection

In the same way, if you are using a built-in class in your query, you can call any of its methods. Here is an example that calls the Concat() of the String class:

namespace HumanResources
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Contractor[] contractors = new Contractor[]
            {
                new Contractor(number : 971974, firstName : "Patricia",  lastName : "Katts",     salary : 24.68M),
                new Contractor(number : 208411, firstName : "Raymond",   lastName : "Kouma",     salary : 20.15M),
                new Contractor(number : 279374, firstName : "Jeannette", lastName : "Mukoko",    salary : 15.55M),
                new Contractor(number : 707912, firstName : "Bertrand",  lastName : "Yamaguchi", salary : 24.68M),
                new Contractor(number : 971394, firstName : "Gertrude",  lastName : "Monay",     salary : 20.55M)
            };

            var empls = from staffMembers
                        in contractors
                        select string.Concat(staffMembers.Code.ToString(), ", ",
                                             staffMembers.FirstName, " ",
                                             staffMembers.LastName, ", ",
                                             staffMembers.HourlySalary.ToString());

            foreach (var empl in empls)
                lbxContractors.Items.Add(empl);
        }
    }

    readonly internal record struct Contractor
    {
        internal int     Code         { get; init; }
        internal string? FirstName    { get; init; }
        internal string? LastName     { get; init; }
        internal decimal HourlySalary { get; init; }

        internal Contractor(int number       = 0,
                            string firstName = "John",
                            string lastName  = "Doe",
                            decimal salary   = 0M)
        {
            Code         = number;
            FirstName    = firstName;
            LastName     = lastName;
            HourlySalary = salary;
        }
    }
}

This would produce:

Employees

A Query as an Object

A Field for a Query

A query starts like any object. Therefore, in the body of a class, you can declare a variable, meaning you can create a field, that represents a query. You can declare such a variable as IEnumerable<> and, in the class, use it as normally as possible.

Using a Read-Only Field for a Query

If you want a query variable whose members cannot be modified by members other than the constructors of the class, you can make the field read-only. In that case, before accessing the field in the members of the class, you must initialize that field in one or more constructors of the class. To do this, in the body of a constructor of the class, you can first declare an array or an IEnumerable<> variable. Then initialize the read-only field with a LINQ statement from the variable. After doing this, you can access the field where necessary in the body of the class. Here is an example:

using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace Numerotation
{
    public partial class Exercise : Form
    {
        internal readonly IEnumerable<State> States;

        public Exercise()
        {
            InitializeComponent();

            State[] entities = new State[6];

            entities[0] = new State() { Abbreviation = "WA", StateName = "Western Australia", AreaSqrKms = 2529875, Capital = "Perth" };
            entities[1] = new State() { Abbreviation = "SA", StateName = "South Australia", AreaSqrKms = 983482, Capital = "Adelaide" };
            entities[2] = new State() { Abbreviation = "QLD", StateName = "Queensland", AreaSqrKms = 1730648, Capital = "Brisbane" };
            entities[3] = new State() { Abbreviation = "NSW", StateName = "New South Wales", AreaSqrKms = 800642, Capital = "Sydney" };
            entities[4] = new State() { Abbreviation = "VIC", StateName = "Victoria", AreaSqrKms = 227416, Capital = "Melbourne" };
            entities[5] = new State() { Abbreviation = "TAS", StateName = "Tasmania", AreaSqrKms = 68401, Capital = "Hobart" };

            States = from gov
                     in entities
                     select gov;
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            int i = 1;

            foreach (var item in States)
            {
                ListViewItem lviStoreItem = new ListViewItem(i.ToString());

                lviStoreItem.SubItems.Add(item.Abbreviation);
                lviStoreItem.SubItems.Add(item.StateName);
                lviStoreItem.SubItems.Add(item.AreaSqrKms.ToString());
                lviStoreItem.SubItems.Add(item.Capital);
                lvwStates.Items.Add(lviStoreItem);

                i++;
            }
        }
    }

    internal interface IAbbreviated
    {
        string? Abbreviation { get; set; }
    }

    public abstract class GovernmentEntity
    {
        public virtual string? StateName { get; set; }
        public virtual int AreaSqrKms { get; set; }
        public virtual string? Capital { get; set; }
    }

    public class State : GovernmentEntity,
                            IAbbreviated
    {
        public string? Abbreviation { get; set; }
    }
}

This would produce:

A Property for a Query

In a class, you can create a property that would hold a query. To start, create the property as an IEnumerable<> object. You can then use a constructor of the class to define the property. As seen with fields, after initializing the property, you can access it where necessary in your project. Here is an example:

using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace Numerotation
{
    public partial class Exercise : Form
    {
        internal IEnumerable<StoreItem> StoreItems { get; set; }

        public Exercise()
        {
            InitializeComponent();

            var sales = new StoreItem[]
            {
                new StoreItem() { ItemNumber = 925173, Category = "Guitars", SubCategory = "Acoustic",
                                  ItemName = "Yamaha F335 Acoustic Guitar", UnitPrice = 129.85 },
                new StoreItem() { ItemNumber = 974951, Category = "Cables", SubCategory = "Microphone Cables",
                                  ItemName = "Live Wire Advantage Standard EXM Series Microphone Cable", UnitPrice = 14.95 },
                new StoreItem() { ItemNumber = 274703, Category = "Keyboards", SubCategory = "Synthesizers",
                                  ItemName = "Access Virus TI v2 Keyboard", UnitPrice = 2992.25 },
                new StoreItem() { ItemNumber = 208148, Category = "Guitars", SubCategory = "Bass",
                                  ItemName = "ESP LTD EC-256FM Electric Guitar", UnitPrice = 395.95 },
                new StoreItem() { ItemNumber = 274930, Category = "Drum Sets", SubCategory = "Acoustic",
                                  ItemName = "Ludwig Accent Combo with Zildjian ZBT Cymbal Set", UnitPrice = 595.85 },
                new StoreItem() { ItemNumber = 802840, Category = "Guitars", SubCategory = "Acoustic",
                                  ItemName = "Taylor Big Baby Taylor Acoustic Guitar", UnitPrice = 449.95 },
                new StoreItem() { ItemNumber = 408408, Category = "Speakers", SubCategory = "Speaker System",
                                  ItemName = "Behringer EUROLIVE B210D Active PA Speaker System", UnitPrice = 195.95 },
                new StoreItem() { ItemNumber = 524037, Category = "Guitars", SubCategory = "Bass",
                                  ItemName = "Fender Deluxe P Bass Special 4-String Bass", UnitPrice = 699.95 },
            };

            StoreItems = from items
                         in sales
                         select items;
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            foreach (var item in StoreItems)
            {
                ListViewItem lviStoreItem = new ListViewItem(item.ItemNumber.ToString());

                lviStoreItem.SubItems.Add(item.Category);
                lviStoreItem.SubItems.Add(item.SubCategory);
                lviStoreItem.SubItems.Add(item.ItemName);
                lviStoreItem.SubItems.Add(item.UnitPrice.ToString());
                lvwStoreItems.Items.Add(lviStoreItem);
            }
        }
    }

    internal record StoreItem
    {
        internal int     ItemNumber  { get; init; }
        internal string? Category    { get; init; }
        internal string? SubCategory { get; init; }
        internal string? ItemName    { get; init; }
        internal double  UnitPrice   { get; init; }
    }
}

This would produce:

Returning a Query

As you are aware already, you can create you own class and use its objects as items for a query. As a matter of fact, you can create a function that returns a query that holds objects of a class. In the function, you can declare a var or an IEnumerable<> variable, initialize it with a LINQ statement, and then return that variable. Where you need the query, declare a variable and initialize it by calling the function. Here is an example:

namespace SoloMusicStore
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private IEnumerable<StoreItem> Create()
        {
	    // A collection of records
            var storeItems = new StoreItem[]
            {
                new StoreItem(925173, "Guitars", "Acoustic", "Yamaha F335 Acoustic Guitar", 129.85),
                new StoreItem(974951, "Cables", "Microphone Cables", "Live Wire Advantage Standard EXM Series Microphone Cable", 14.95),
                new StoreItem(274703, "Keyboards", "Synthesizers", "Access Virus TI v2 Keyboard", 2992.25),
                new StoreItem(208148, "Guitars", "Bass", "ESP LTD EC-256FM Electric Guitar", 395.95),
                new StoreItem(274930, "Drum Sets", "Acoustic", "Ludwig Accent Combo with Zildjian ZBT Cymbal Set", 595.85),
                new StoreItem(802840, "Guitars", "Acoustic", "Taylor Big Baby Taylor Acoustic Guitar", 449.95),
                new StoreItem(408408, "Speakers", "Speaker System", "Behringer EUROLIVE B210D Active PA Speaker System", 195.95),
                new StoreItem(524037, "Guitars", "Bass", "Fender Deluxe P Bass Special 4-String Bass", 699.95),
            };

            IEnumerable<StoreItem> sale = from items
                                          in storeItems
                                          select items;
            return sale;
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            var sale = Create();

            foreach (var item in sale)
            {
                ListViewItem lviStoreItem = new ListViewItem(item.ItemNumber.ToString());

                lviStoreItem.SubItems.Add(item.Category);
                lviStoreItem.SubItems.Add(item.SubCategory);
                lviStoreItem.SubItems.Add(item.ItemName);
                lviStoreItem.SubItems.Add(item.UnitPrice.ToString());
                lvwStoreItems.Items.Add(lviStoreItem);
            }
        }
    }

    internal record StoreItem
    {
        internal int     ItemNumber  { get; init; }
        internal string? Category    { get; init; }
        internal string? SubCategory { get; init; }
        internal string? ItemName    { get; init; }
        internal double  UnitPrice   { get; init; }

        public StoreItem(int     number,
                         string? type,
                         string? sub,
                         string? name,
                         double  price)
        {
            SubCategory = sub;
            Category    = type;
            ItemName    = name;
            UnitPrice   = price;
            ItemNumber  = number;
        }
    }
}

In the above code, we declared a variable that the function would return. Remember that such a variable is necessary only you are planning to use it many times. Otherwise, you can write the query statement directly where it is returned. Here is an example:

IEnumerable<StoreItem> Create()
{
    var storeItems = new StoreItem[]
    {
	// ... No change
    };

    return from items in storeItems select items;
}

Passing a Query as Argument

A parameter of a function can be treated locally as a query statement. There is nothing magical with that. When creating the function, create an IEnumerable<> object in its parentheses. In the body of the function, ignore or use the parameter. When calling the function, pass a real query statement to it. You can pass it as a variable that holds the query. Here is an example:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private IEnumerable<StoreItem> Create()
        {
            var storeItems = new StoreItem[]
            {
                // . . . No change
            };

            return from items
                   in storeItems
                   select items;
        }

        private void Show(IEnumerable<StoreItem> holder)
        {
            foreach (var item in holder)
            {
                ListViewItem lviStoreItem = new ListViewItem(item.ItemNumber.ToString());

                lviStoreItem.SubItems.Add(item.Category);
                lviStoreItem.SubItems.Add(item.SubCategory);
                lviStoreItem.SubItems.Add(item.ItemName);
                lviStoreItem.SubItems.Add(item.UnitPrice.ToString());
                lvwStoreItems.Items.Add(lviStoreItem);
            }
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            var sale = Create();

            Show(sale); // You can also use: Show(Create()); 
        }
    }

    internal record StoreItem
    {
        internal int     ItemNumber  { get; init; }
        internal string? Category    { get; init; }
        internal string? SubCategory { get; init; }
        internal string? ItemName    { get; init; }
        internal double  UnitPrice   { get; init; }

        public StoreItem(int number, string? type, string? sub, string? name, double price)
        {
            (SubCategory, Category, ItemName, UnitPrice, ItemNumber) = (sub, type, name, price, number);
        }
    }
}

Operations on a Query

Checking a Condition

As a member of a class can be used in a query, you can apply a condition to a particular field or property to check something. The formula to follow is:

var | IEnumerable<...> sub-list-name = from value-holder
		in list
            	where value-holder.class-member condition
		select value-holder;

All the Boolean operators (==, !=, <, <=, >, and >=) are available. The logical operators !, is, and not are also available. The logical rules for the condition are the same we reviewed already. Here is an example:

var vdos = from   videos
           in     lstVideos
           where  videos.WideScreen == true
           select videos.GetVideo();

This would produce:

Video Collection

Remember that, to negate a condition, precede it with the ! operator. Here is an example:

var vdos = from   videos
           in     lstVideos
           where  !(videos.WideScreen == true)
           select videos.GetVideo();

This would produce:

Negating a Condition

In the same way, you can negate a where condition that involves any appropriate value.

Sorting With Class

Sorting the members of a primitive-based list is quite easy. This is because the structure of each data type implements the IComparable interface. Since each member of a class holds a category of value, you can use a class member as a basis to arrange how a query displays its values, in alphabetical order, in chronological order, etc. Remember that the keyword to sort records is orderdy. You can apply that operator to the class member of your choice. Here is an example:

var vdos = from videos
           in lstVideos
           orderby videos.Title
           select videos;

By default, a list is sorted in alphabetical, chronological, or incrementing, etc, order. This is referred to as ascending order. In fact, to indicate it, you can follow the orderby statement with the ascending keyword. Here is an example:

var vdos = from videos
           in lstVideos
           orderby videos.Title ascending
           select videos;

On the other hand, if you want to sort records in reverse order, apply the descending keyword to your orderby statement in place of the ascending keyword. Here is an example:

var vdos = from videos
           in lstVideos
	   orderby videos.YearReleased descending
           select videos;

If necessary, you can use a where condition when sorting the records. This allows you to sort only restricted list of records. Here is an example:

namespace VideoCollection
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Video[] lstVideos = new Video[]
        {
            new Video(274880, "Platoon", "R", 1986, false),
            new Video(792075, "Two for the Money", "R", 2008, true),
            new Video(283748, "Cousin Vinny (My)", "R", 1992, false),
            new Video(593940, "Natural Born Killers", "R", 1994, true),
            new Video(900245, "Her Alibi", "PG-13", 1998, false),
            new Video(773022, "Distinguished Gentleman (The)", "R", 1992, false),
            new Video(961973, "Wall Street", "R", 2000, false),
            new Video(180358, "Memoirs of a Geisha", "PG-13", 2006, true)
        };

            var vdos = from videos
                       in lstVideos
                       orderby videos.YearReleased
                       where videos.WideScreen == false
                       select videos;

            foreach (var vdo in vdos)
            {
                ListViewItem lviVideo = new ListViewItem(vdo.ShelfNumber.ToString());

                lviVideo.SubItems.Add(vdo.Title);
                lviVideo.SubItems.Add(vdo.Rating);
                lviVideo.SubItems.Add(vdo.YearReleased.ToString());
                lviVideo.SubItems.Add(vdo.WideScreen.ToString());
                lvwVideos.Items.Add(lviVideo);
            }
        }
    }

    internal readonly record struct Video
    {
        internal int     ShelfNumber  { get; init; }
        internal string? Title        { get; init; }
        internal string? Rating       { get; init; }
        internal int     YearReleased { get; init; }
        internal bool    WideScreen   { get; init; }

        public Video(int id, string title, string rating, int year, bool hd)
        {
            ShelfNumber  = id;
            Title        = title;
            Rating       = rating;
            YearReleased = year;
            WideScreen   = hd;
        }
    }
}

This would produce:

Video Collection

The orderby statement can come before or after the where condition.

Conjunctions and Classes/Records/Structures

We already know that we can use a list of objects in a query. When it comes to a conjunction, you can start by creating a list of objects. Then, in one or both conditions of a conjunction in your query, you can access one or more members of the class, record, or structure, and perform the comparison of your choice. Each condition can apply to a different member of the class, record, or structure. Here is an example:

namespace VideoCollection
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Video[] lstVideos = new Video[]
        {
            new Video(274880, "Platoon", "Oliver Stone", "R", 1986, false),
            new Video(792075, "Two for the Money", "D.J. Caruso", "R", 2008, true),
            new Video(283748, "Cousin Vinny (My)", "Jonathan Lynn", "R", 1992, false),
            new Video(593940, "Natural Born Killers", "Oliver Stone", "R", 1994, true),
            new Video(900245, "Her Alibi", "Bruce Beresford", "PG-13", 1998, false),
            new Video(773022, "Distinguished Gentleman (The)", "Jonathan Lynn", "R", 1992, false),
            new Video(961973, "Wall Street", "Oliver Stone", "R", 2000, false),
            new Video(180358, "Memoirs of a Geisha", "Rob Marshall", "PG-13", 2006, true)
        };

            var vdos = from videos
                       in lstVideos
                       where videos.Director == "Oliver Stone" & videos.WideScreen == false
                       select videos;

            foreach (var vdo in vdos)
            {
                ListViewItem lviVideo = new ListViewItem(vdo.ShelfNumber.ToString());

                lviVideo.SubItems.Add(vdo.Title);
                lviVideo.SubItems.Add(vdo.Director);
                lviVideo.SubItems.Add(vdo.Rating);
                lviVideo.SubItems.Add(vdo.YearReleased.ToString());
                lviVideo.SubItems.Add(vdo.WideScreen.ToString());
                lvwVideos.Items.Add(lviVideo);
            }
        }
    }

    internal readonly record struct Video
    {
        internal int    ShelfNumber  { get; init; }
        internal string Title        { get; init; }
        internal string Director     { get; init; }
        internal string Rating       { get; init; }
        internal int    YearReleased { get; init; }
        internal bool   WideScreen   { get; init; }

        internal Video(int id, string title, string director, string rating, int year, bool hd)
        {
            ShelfNumber  = id;
            WideScreen   = hd;
            YearReleased = year;
            Title        = title;
            Rating       = rating;
            Director     = director;
        }
    }
}

This would produce:

Video Collection

Remember that, to make the statement easier to read and figure out, you should include each part in its parentheses:

var vdos = from videos
           in lstVideos
           where (videos.Director == "Oliver Stone") & (videos.WideScreen == false)
           select videos;

Or better yet:

var vdos = from videos
           in lstVideos
           where ((videos.Director == "Oliver Stone") && (videos.WideScreen == false))
           select videos;

The result would be the same as above. Remember that, to negate a conjunction, precede it with a ! operator. You can also arrange the list in an order based on a member of the class and using the orderby operator. Here is an example:

var vdos = from videos
           in lstVideos
           where !(videos.Director == "Oliver Stone") && (videos.WideScreen == false)
           orderby videos.YearReleased
           select videos;

This would produce:

Video Collection

Disjunctions and Classes/Records/Structures

A logical disjunction is used to get the result of two or more conditions to find out whether at least one of those conditions produces a false result. The C# language provides three operators to perform this operation: ^, |, and ||. We reviewed these operators already. Here is an example:

var vdos = from videos
           in lstVideos
           where (videos.Director == "Oliver Stone") | (videos.Director == "Jonathan Lynn")
           select videos;

This would produce:

Video Collection

The conditions can also each apply to a different member of the class. Here is an example:

var vdos = from videos
           in lstVideos
           where (videos.Director == "Oliver Stone") || (videos.WideScreen == true)
           select videos;

Remember that you can arrange the result using the orderby keyword applied to the class member of your choice. Here is an example:

var vdos = from videos
           in lstVideos
           where (videos.Director == "Oliver Stone") || (videos.WideScreen == true)
           orderby videos.Title
           select videos;

Tuples and Query Statements

Introduction

As you should know already, a tuple consists of combining two or more types into a common type without creating a formal class, a record, or a structure. You can create a list where each item is a combination of types of a tuple. You can then create a LINQ statement that selects from a tuple-based list. In your select clause, you can select which item of the tuple you will select.

Selecting a Tuple

If you create a query from a list of tuples, each item of the resulting query represents a whole item of the list as they are created in the list. If necessary, you can access each item as a tuple. Here is an example:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            (string day, float time)[] daysWork = new (string, float)[]
            {
                ("Monday", 8.0f),
                ("Tuesday", 9.5f),
                ("Wendnesday", 7.50f),
                ("Thursday", 8.5f),
                ("Friday", 6.00f),
                ("Saturday", 10.5f),
                ("Sunday", 8.50f)
            };

            var week = from result in daysWork
                       select result;

            foreach (var item in week)
            {
                lbxDaysWork.Items.Add(item);
            }
        }
    }
}

This would produce:

LINQ and Tuples

Selecting an Item from a Tuple

As seem above, a query based on a list of tuples prodcues a collection of tuples. This allows you to process each item as one from a tuple. You have two options. As one solution, after creating your query statement, in your loop, use the variable on the foreach operation to select the desired item of the tuple. This can be done as follows:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            (string day, float time)[] daysWork = new (string, float)[]
            {
                ("Monday", 8.0f),
                ("Tuesday", 9.5f),
                ("Wendnesday", 7.50f),
                ("Thursday", 8.5f),
                ("Friday", 6.00f),
                ("Saturday", 10.5f),
                ("Sunday", 8.50f)
            };

            var week = from result in daysWork
                       select result;

            foreach (var item in week)
            {
                lbxDaysWork.Items.Add(item.day);
            }
        }
    }
}

This would produce:

LINQ and Tuples

As an alternative, when creating your query statement, you can make your select operator identify the tuple item you want. This can be done as follows:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            (string day, float time)[] daysWork = new (string, float)[]
            {
                ("Monday", 8.0f),
                ("Tuesday", 9.5f),
                ("Wendnesday", 7.50f),
                ("Thursday", 8.5f),
                ("Friday", 6.00f),
                ("Saturday", 10.5f),
                ("Sunday", 8.50f)
            };

            var week = from result in daysWork
                       select result.day;

            foreach (var item in week)
            {
                lbxDaysWork.Items.Add(item);
            }
        }
    }
}

Selecting Each Item of a Tuple

In some cases, you may want to access each item of a tuple but use them individually. You have two options. You can make your query produce parenthal members that follow the same format as the tuple. Here is an example:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            (DateOnly date, string day, float work)[] daysWork = new (DateOnly, string, float)[]
            {
                (new DateOnly(2023, 1, 30), "Monday", 8.0f),
                (new DateOnly(2023, 1, 31), "Tuesday", 9.5f),
                (new DateOnly(2023, 2,  1), "Wendnesday", 7.50f),
                (new DateOnly(2023, 2,  2), "Thursday", 8.5f),
                (new DateOnly(2023, 2,  3), "Friday", 6.00f),
            };

            var week = from result
                       in daysWork
                       select (result.date, result.day, result.work);

            foreach (var item in week)
            {
                lbxDaysWork.Items.Add(item);
            }
        }
    }
}

This would produce:

LINQ and Tuples

An alternative is to make the select statement produce the whole list. Then, in your loop, access each item of the tuple. Here is an example:

namespace Numerotation
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            (DateOnly date, float work)[] daysWork = new (DateOnly, float)[]
            {
                (new DateOnly(2023, 1, 30), 8.0f),
                (new DateOnly(2023, 1, 31), 9.5f),
                (new DateOnly(2023, 2,  1), 7.50f),
                (new DateOnly(2023, 2,  2), 8.5f),
                (new DateOnly(2023, 2,  3), 6.00f),
            };

            var week = from result in daysWork
                       select result;

            foreach (var item in week)
            {
                ListViewItem lviPayroll = new ListViewItem(item.date.ToString("dd-MMM-yyyy"));

                lviPayroll.SubItems.Add(item.date.DayOfWeek.ToString());
                lviPayroll.SubItems.Add(item.work.ToString("N"));
                lvwPayroll.Items.Add(lviPayroll);
            }
        }
    }
}

This would produce:

LINQ and Tuples

In the same way, you can create a tuple with as many items as you judge necessary and involve it in your LINQ query.


Previous Copyright © 2008-2023, FunctionX, Inc. Sunday 05 November 2022 Next