Monthly Archives: August 2016

Returning a collection of objects from a PowerShell function

If you’re writing a function that returns a collection then don’t forget to include the comma operator in the return statement. If you forget it your function will work correctly when the collection contains multiple objects, but fails when it contains 1 object.

Take the following buggy example:

function GiveMeAllTheThings()
{
    $myarray = @()
    #fill $myarray with results of type String. Assume that
    #run-time conditions determine if it is filled with 
    #0, 1 or more items and that each item is a string
    return $myarray;
}
$result = GiveMeAllTheThings
$result.GetType().FullName

If you execute this code when $myarray has many strings in it, the returned type from the function is System.Object[]. If $myarray has only 1 string in it, then the returned type will be System.String.

The code should have been written like this:

function GiveMeAllTheThings()
{
    ...
    return ,$myarray;
}

Understanding the LINQ nested grouping example

Here’s an explanation of how the default example for LINQ nested grouping actually works. The usual example for nested grouping looks like this:

from student in Students
group student by student.Faculty into Faculty
from dbtgroup in
(
    from student in Faculty
    group student by student.DebtCategory
)
group dbtgroup by Faculty.Key;

The objective of this statement is to first group-by students into faculties and then in each faculty create subgroupings of students by their DebtCategory.

So how does this actually work and whats the equivalent method/lamba syntax? The first step is to groups each student into their faculty. Assume we have the following data

public class Student
{
   public string Name { get; set; }
   public string Faculty { get; set; }
   public int DebtCategory { get; set; }
}

IList<Student> Students = new List<Student>();
Students.Add(new Student { Name = "John" , Faculty = "IT"     , DebtCategory = 2 });
Students.Add(new Student { Name = "Jane" , Faculty = "IT"     , DebtCategory = 2 });
Students.Add(new Student { Name = "Jesse", Faculty = "Finance", DebtCategory = 2 });
Students.Add(new Student { Name = "Linda", Faculty = "Finance", DebtCategory = 1 });

The following query groups each student into a faculty

var query1 = from student in Students
group student by student.Faculty into Faculty
select Faculty;

//The Method syntax for the above query is:
var query1Method = Students
.GroupBy(student => student.Faculty)
.Select ( Faculty => Faculty);

//This gives us the following IGrouping<string, Student> as result
//
// [0]
//    Key   :  IT
//    Values: 
//          [0] John (IT) (2)
//          [1] Jane (IT) (2)
//
// [1]
//    Key   : Finance
//    Values:
//          [0] Jesse (Finance) (2)
//          [1] Linda (Finance) (1)

The next step is to add another level of grouping:

var query2 = from student in Students
group student by student.Faculty into Faculty
from dbtgroup in
(
    from student in Faculty
    group student by student.DebtCategory
)
select dbtgroup;
//This gives us the following IGrouping<int, Student> as result
//[0]
//  Key   : 2
//  Values:
//        [0] John (IT) (2)
//        [1] Jane (IT) (2)
//
//[1]
//  Key   : 2
//  Values:
//        [0] Jesse (Finance) (2)
//
//[2]
//  Key   : 1
//  Values:
//        [0] Linda (Finance) (1)

// The following is the literal translation of the above Comprehension syntax into method syntax. We're ignoring this as explained below
//	var query2Method = Students
//		.GroupBy(student => student.Faculty)
//		.SelectMany(  Faculty =>Faculty.GroupBy(student => student.DebtCategory)
//					, (Faculty, dbtgroup) => dbtgroup);
	
//The final complete query ends with"group dbtgroup by Faculty.Key;" 
// this statement causes the compiler to see that you're refering to the Faculty object from the select many, so instead of 
// "(Faculty, dbtgroup) => dbtgroup" it emits a slightly different projection "(Faculty, dbtgroup) => new {Faculty, dbtgroup}
//structure
var query2Method = Students
.GroupBy(student => student.Faculty)
.SelectMany( Faculty =>Faculty.GroupBy(student => student.DebtCategory)
	 , (Faculty, dbtgroup) => new {Faculty, dbtgroup});

Query2 is close to our desired output, however the grouping is the wrong way around. So the final step is:

var query3 = from student in Students
group student by student.Faculty into Faculty
from dbtgroup in
    (
    from student in Faculty
    group student by student.DebtCategory
    )
group dbtgroup by Faculty.Key;

//The method/lambda syntax is:
var query3Method = Students
.GroupBy(student => student.Faculty)
.SelectMany (
	Faculties => Faculties.GroupBy (student => student.DebtCategory)
	, (Faculty, dbtgroup) => 
		new  
		{
			Faculty = Faculty, 
			dbtgroup = dbtgroup
		} )
.GroupBy( item => item.Faculty.Key, item => item.dbtgroup );

//This gives us the following groups as result
//[0]
//  Key   : IT
//  Values:
//        [0] Key   : 2
//            Values:
//                  [0] John (IT) (2)
//                  [1] Jane (IT) (2)
//[1]
//  Key   : Finance
//  Values:
//        [0] Key   : 2
//            Values:
//                  [0] Jesse (Finance) (2)
//        [1] Key   : 1
//            Values:
//                    [0] Linda (Finance) (1)