Sorting lists of AutoCAD objects using LINQ – Part 2

In the last post we took a look at one technique to sort a list of ObjectIds by the associated objects' layer and type. In this post we're going to refactor the commonality to have a single extension method that allows us to sort a list of ObjectIds based on any string property.

Here's the updated C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Collections.Generic;

using System.Linq;

 

namespace ProcessObjectsInOrder

{

  public static class Extensions

  {

    /// <summary>

    /// Sorts an array of ObjectIds based on a string property and order.

    /// </summary>

    /// <param name="ids">The array of IDs to sort.</param>

    /// <param name="propertySelector">A function selecting the string property.</param>

    /// <param name="orderSelector">A function to specify the selection order.</param>

    /// <returns>An ordered enumerable of key-value pairs.</returns>

 

    public static List<KeyValuePair<ObjectId, string>>

      Sort(

        this ObjectId[] ids,

        Func<dynamic, string> propertySelector,

        Func<KeyValuePair<ObjectId, string>, string> orderSelector

      )

    {

      var map = new Dictionary<ObjectId, string>();

 

      foreach (dynamic id in ids)

      {

        map.Add(id, propertySelector(id));

      }

 

      return map.OrderBy(orderSelector).ToList();

    }

  }

 

  public class Commands

  {

    [CommandMethod("OBL", CommandFlags.UsePickSet)]

    public static void ObjectsByLayer()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      // Select the objects to sort

 

      var psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK)

        return;

 

      // We'll sort them based on a string value (the layer name)

 

      var map = new Dictionary<ObjectId, string>();

 

      foreach (dynamic id in psr.Value.GetObjectIds())

      {

        map.Add(id, id.Layer);

      }

 

&
nbsp;     var sorted = map.OrderBy(kv => kv.Value);

 

      // Print them in order to the command-line

 

      foreach (var item in sorted)

      {

        ed.WriteMessage("\nObject {0} on layer {1}", item.Key, item.Value);

      }

    }

 

    [CommandMethod("OBT", CommandFlags.UsePickSet)]

    public static void ObjectsByType()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      // Select the objects to sort

 

      var psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK)

        return;

 

      // Sort them based on a string value (the class name)

 

      var map = new Dictionary<ObjectId, string>();

 

      foreach (dynamic id in psr.Value.GetObjectIds())

      {

        map.Add(id, id.ObjectClass.Name);

      }

 

      var sorted = map.OrderBy(kv => kv.Value);

 

      // Print them in order to the command-line

 

      foreach (var item in sorted)

      {

        ed.WriteMessage("\nObject {0} of type {1}", item.Key, item.Value);

      }

    }

 

    [CommandMethod("OBL2", CommandFlags.UsePickSet)]

    public static void ObjectsByLayer2()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      // Select the objects to sort

 

      var psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK)

        return;

 

      // Sort them based on a string value (the layer name)

 

      var sorted = psr.Value.GetObjectIds().Sort(id => id.Layer, kv => kv.Value);

 

      // Print them in order to the command-line

 

      foreach (var item in sorted)

      {

        ed.WriteMessage("\nObject {0} on layer {1}", item.Key, item.Value);

      }

    }

 

    [CommandMethod("OBT2", CommandFlags.UsePickSet)]

    public static void ObjectsByType2()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      // Select the objects to sort

 

      var psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK)

        return;

 

      // Sort them based on a string value (the class name)

 

      var sorted =

        psr.Value.GetObjectIds().Sort(id => id.ObjectClass.Name, kv => kv.Value);

 

      // Print them in order to the command-line

 

      foreach (var item in sorted)

      {

        ed.WriteMessage("\nObject {0} of type {1}", item.Key, item.Value);

      }

    }

  }

}

 

The extension method takes a couple of function parameters: one to select the property to sort on and another to indicate the order.

Here are the new versions of the commands – OBL2 and OBT2 – in action, to make sure they work in the same way as the last ones.

Sort objects by property - extension method

James Maeding made the suggestion of ordering the list directly. That certainly makes sense, if you don't care about accessing the property the list was sorted on (or don't mind making the call to query the property again).

I think that in many cases it'll be important to access the sorting criterion while processing the object, so I've decided to leave the implementation as it is. Thanks for the comment, though, James – I can imagine plenty of scenarios where your approach would make sense.

In the next post we'll convert our extension method into a static template class, allowing us to sort on different property types (such as the color index of an object's layer).

I'm on vacation up in the mountains, next week, but I'll at least post the next part (and may well post more – we'll see how it goes).

2 responses to “Sorting lists of AutoCAD objects using LINQ – Part 2”

  1. ok, now my head is swimming. I'm not good enough with the => and Func stuff to author code like that, though I get what its doing. Given that fact, am I losing out on speed by sorting without the extension method?
    I don't mind less compact code, but am always watching for tricks that speed up sorting as a few of my programs are bottlenecked by sorting speed.

    1. I don't think you'll see better performance, per se. It's just a way of packaging code to make it cleaner.

      Kean

Leave a Reply to Kean Walmsley Cancel reply

Your email address will not be published. Required fields are marked *