简体   繁体   中英

Sorting the Named Ranges in Excel's Name Manager with code

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:

在此处输入图片说明

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

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 .)

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM