Question

I get a List of Named Ranges from an Excel spreadsheet using VSTO:

public List<Name> GetNamedRanges(Workbook activeWorkbook)
{
    List<Name> namedRanges = new List<Name>();
    Name name;
    for (int i = 0; i < activeWorkbook.Names.Count; i++)
    {
        name = activeWorkbook.Names.Item(i + 1);
        if (!name.Value.Contains("#REF"))
        {
            namedRanges.Add(name);
        }       
    }
    return namedRanges;
}

This returns the Names in a weird order:

enter image description here

Does anyone have a simple method to sort the Name Ranges by Column Order. eg:

=Sheet1!$A$9:$B$172
=Sheet1!$C$9:$D$172
=Sheet1!$E$41:$F$172

Was it helpful?

Solution 3

I just it did by removing digits, then ordering by Length and then by Alphabetical, its pretty messy but does the job:

static public List<Name> GetNamedRangesInOrder(Workbook activeWorkbook)
{
    List<Name> namedRanges = GetNamedRanges(activeWorkbook);

    List<string> lstStringNameRanges = new List<string>();
    foreach (var item in namedRanges)
    {
        lstStringNameRanges.Add(RemoveDigits(item.RefersTo.ToString()));
    }

    IEnumerable<string> results = SortByLengthAndName(lstStringNameRanges);
    List<Name> sortedNamedRanges = new List<Name>();
    foreach (var item in results)
    {
        int index = -1;
        for (int i=0; i < namedRanges.Count; i++)
        {
            if (RemoveDigits(namedRanges[i].RefersTo.ToString()) == item.ToString())
            {
                index = i;
                break;
            }
        }
        sortedNamedRanges.Add(namedRanges[index]);
    }
    return sortedNamedRanges;

}

static public IEnumerable<string> SortByLengthAndName(IEnumerable<string> e)
{
    IEnumerable<string> query = e.OrderBy(x => x.Length).ThenBy(x => x).ToList();
    return query;
}

static public string RemoveDigits(string e)
{
    string str = new string((from c in e
            where char.IsLetter(c) || char.IsSymbol(c)
            select c).ToArray());

    return str;
}

OTHER TIPS

Took the code from http://www.interact-sw.co.uk/iangblog/2007/12/13/natural-sorting and modified it so it orders by string length (after splitting by numbers) followed by natural ordering. The limitation is that if you do have sheets of different names, your sheets would also be sorted by string length as opposed to asciinumerical.

public class ExcelNameComparer<T> : IComparer<IEnumerable<T>>
{
    /// <summary>
    /// Create a sequence comparer using the default comparer for T.
    /// </summary>
    public ExcelNameComparer()
    {
        comp = Comparer<T>.Default;
    }

    /// <summary>
    /// Create a sequence comparer, using the specified item comparer
    /// for T.
    /// </summary>
    /// <param name="comparer">Comparer for comparing each pair of
    /// items from the sequences.</param>
    public ExcelNameComparer(IComparer<T> comparer)
    {
        comp = comparer;
    }

    /// <summary>
    /// Object used for comparing each element.
    /// </summary>
    private IComparer<T> comp;


    /// <summary>
    /// Compare two sequences of T.
    /// </summary>
    /// <param name="x">First sequence.</param>
    /// <param name="y">Second sequence.</param>
    public int Compare(IEnumerable<T> x, IEnumerable<T> y)
    {
        using (IEnumerator<T> leftIt = x.GetEnumerator())
        using (IEnumerator<T> rightIt = y.GetEnumerator())
        {
            while (true)
            {
                bool left = leftIt.MoveNext();
                bool right = rightIt.MoveNext();

                if (!(left || right)) return 0;

                if (!left) return -1;
                if (!right) return 1;

                int lengthResult = leftIt.Current.ToString().Length.CompareTo(rightIt.Current.ToString().Length);
                if (lengthResult != 0) return lengthResult;

                int itemResult = comp.Compare(leftIt.Current, rightIt.Current);
                if (itemResult != 0) return itemResult;
            }
        }
    }

Func<string, object> convert = str =>
{
    try { return int.Parse(str); }
    catch { return str; }
};

Now running the actual sort:

    var lst = new List<string> { "Sheet1!$A$9:$B$172", "Sheet1!$AY$77:$AZ$172",     "Sheet1!$E$41:$F$172", "Sheet1!$A$10:$B$172", "Sheet1!$A$1:$B$172" };

    var sorted = lst.OrderBy(
        str => Regex.Split(str.Replace(" ", ""), "([0-9]+)").Select(convert),
        new ExcelNameComparer<object>());
    foreach (var sort in sorted)
    {
        System.Diagnostics.Debug.Print(sort);
    }

Yields:

Sheet1!$A$1:$B$172
Sheet1!$A$9:$B$172
Sheet1!$A$10:$B$172
Sheet1!$E$41:$F$172
Sheet1!$AY$77:$AZ$172

Sort them in Excel and then read in the Workbook again. (Is a simple method .)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top