Страницы: 1
RSS
Как определить буквы столбца по его номеру, VBA. Range.Columns. Number To Letter(s)
 
Приветствую!
Продолжаю искать самые скоростные приёмы работы в VBA
Вывод: общая скорость работы, а также двукратный выигрыш на первых 26 однобуквенных столбцах позволяет сказать, что предпочтительнее использовать рекурсивную функцию, в качестве основной  :idea:
Изменено: Jack Famous - 24.05.2021 14:21:14
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Здравствуйте, может этим методом можно определить. Application.ConvertFormula method (Excel)
Добавлено:
Код
Sub Number2Letter()
'PURPOSE: Convert a given number into it's corresponding Letter Reference
'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault

Dim ColumnNumber As Long
Dim ColumnLetter As String

'Input Column Number
  ColumnNumber = InputBox("Type column number")

'Convert To Column Letter
  ColumnLetter = Split(Cells(1, ColumnNumber).Address, "$")(1)
  
'Display Result
  MsgBox "Column " & ColumnNumber & " = Column " & ColumnLetter
  
End Sub
Нашел на зарубежном сайте.
Изменено: DANIKOLA - 25.05.2021 07:17:39
 
DANIKOLA, ну попробуйте  :D
Не понимаю, причём тут конвертация формулы, но с нетерпением жду результатов сравнения по скорости  ;)

Инфо
Дополнил и автоматизировал процесс сравнения (тестовый стенд) — есть кнопка в левом верхнем углу
Попробовал написать универсальную функцию (ColNumToLtr), использующую две других, в зависимости от номера столбца, но безуспешно — по-прежнему проще и быстрее использовать рекурсивную функцию  :idea:

UPD:
нашёл архивную тему - способы, описанные там и близко по скорости не сравнятся с описанными + единственный интересный вариант (от Казанского) некорректно преобразовывает некоторые числа (например, 26) — добавил в коды

UPD2:
DANIKOLA, Ваш метод есть в списке и он самый медленный из тестируемых и один из самых очевидных
Кроме того, он не имеет отношения к ConvertFormula - не вводите в заблуждение и откорректируйте сообщение
И спасибо, что прямо отвечаете на вопрос  ;)
Изменено: Jack Famous - 25.05.2021 08:08:08
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
однако...
Код
Function NColumnToLtr$(c&)
  Dim a$
  If c > Columns.Count Then Exit Function Else a = Cells(1, c).Address(0, 0)
  NColumnToLtr = Left(a, Len(a) - 1)
End Function
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Ігор Гончаренко, однако …
Код
Function ByAddress_Left(ByVal nCol&) As String  ' выбывает из-за медлительности
Dim tx$
    tx = Columns(nCol).Address(0, 0, xlA1)
    ByAddress_Left = Left$(tx, InStr(tx, ":") - 1)
End Function
    • эта функция более 3ёх раз медленнее, чем 3 финальные
    • при этом сравнима с вашей по скорости
    • НЕ только для первых 26 столбцов из одной буквы, в отличие от вашей

Ну и в целом, если есть If c > Columns.Count, то почему нет If c < 1?
Изменено: Jack Famous - 24.05.2021 15:12:06
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
А для чего? Неужели есть такая необходимость - обрабатывать в коде десятки-сотни тысяч раз определение имени столбца, а не его номера?
 
Цитата
vikttur: А для чего?
с неохотой ждал этого вопроса, но думал, что его задаст Миша  :D
Эта тема, как и некоторые другие мои темы - одна цепочка разработки ускорения работы с диапазонами (если Виталий напишет эту функцию, то будет отлично, но альтернативы должны быть, да и интересно это)

Отвлёкся… Нужно это для отбора ячеек по критерию, в качестве замены штатной Range.Address — она не очень шустрая
Вот тут Андрей как раз этот подход использует (собственно, после этого я задумал исследовать этот момент и использовать), …
Код
colChar = Chr$(64 + i)
curAddress = colChar & curRow
… и он даёт ощутимый прирост, т.к., если область большая, то гораздо быстрее использовать счётчик для строк и подобное преобразование для столбцов, чем для каждой подходящей ячейки получать Cell.Address

На всякий случай: использовать стиль ссылок R1C1, чтобы не вычислять буквы столбца не получится, т.к. диапазон из такого адреса не сформировать
Изменено: Jack Famous - 25.05.2021 08:11:26
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
как в формулах предпочитаю R1C1
так же и в VBA при необходимости обратиться к ячейке, диапазону, постоянно использую Сells(r,c) и практически никогда Range("ADF12"), поэтому был слегка удивлен темой
Цитата
Jack Famous написал:
даёт ощутимый прирост, т.к., если область большая,
а после этой фразы - сильно удивлен. а можно пример, где это реально потребовалось, в какой практической задаче? и код, как это было реализовано?
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Цитата
Jack Famous написал: его задаст Миша
я зарекся не топить твои темы  :D

Алексей, если абстрагироваться от задачи , то вопрос в реализации наибыстрого преобразовния десятичного числа в систему счисления по основанию N, где N в данном случае 26. тут нужно реально взвешивать затраты на расчет и на написание. например более краткий чем у Игоря вариант
Код
Function NColumnToLtr$(c&)
  If c > Columns.Count Then Exit Function Else NColumnToLtr = Replace(Cells(1, c).Address(0, 0),"1","")
End Function

но будет чуть дольше и я согласен с Игорем и Виктором, не понятен смысл упражнения. Некий МШ  - давайте у кого быстрее, а зачем придумаем потом.
По вопросам из тем форума, личку не читаю.
 
БМВ, ByAddress_Left разве не быстрее из той же оперы?
Цитата
БМВ: зачем придумаем потом
раньше я собирал адреса по подходящим данным из массива как rng.Areas(n).Cells(r,c).Address - для каждой подходящей ячейки. Теперь мне достаточно определить номер столбца первой ячейки в каждой области и далее счётчик и эта функция преобразования позволит получить тот же адрес, но быстрее. Чем больше области, тем заметнее будет выигрыш
Всё. В этом для меня сейчас и заключена практическая полезность.
Рассматривать тему как простое соревнование ХЗ зачем - меня тоже устроит, т.к. главное выяснить, все ли способы я перебрал и нет ли пошустрее варианта

UPD2 (всем):
Если не поняли, зачем это нужно, то прошу больше не искать конечную цель
Вы вправе считать, что всё можно сделать не так, а гораздо проще, легче и быстрее  В доказательство своих слов я дал ссылки на свои темы с тестами, поэтому, если можете доказать, то прошу это делать исключительно на языке кода и в соответствующих местах (темах).
Можете считать эту тему соревнованием без какой-либо цели. Просто "кто быстрее"
Изменено: Jack Famous - 25.05.2021 08:09:20
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
но будет чуть дольше
Я не писал что быстрее ,я писал что еще короче.
По вопросам из тем форума, личку не читаю.
 
Цитата
Jack Famous написал:
rng.Areas(n).Cells(r,c).Address - для каждой ячейки.
- для каждой ячейки! вот же ж круто! а зачем?
я всегда получал адрес области так:
rng.Areas(n).Address
Areas - это Range и Address - возвращает адрес этого диапазона ячеек (не нужно вычислять 1-ю, не нужно калькулировать с Count не нужно считать по одной)

появляются вопросы? формулируйте задачу, которую решаете, а не способ, которым придумали ее решать
кто-то поймет суть задачи - напишет ответ
а пока никто не понимает ДЛЯ ЧЕГО НУЖНО вычислять буквенное имя столбца, никто и не подумает его вычислять
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Когда-то писал для XLL (для всего адреса)
Скрытый текст

Все же перевел (specially for you) на VBA в части определения буквы столбца (проверь по скорости со своими решениями)
Код
Sub test()
Debug.Print R1C1toA1(1, 16000)
End Sub

Function R1C1toA1(rw As Long, col As Long) As String  'номер столбца в букву
Dim A1(0 To 5) As Byte, i As Long, col3 As Long, col2 As Long, col1 As Long
    i = 0
    col3 = col \ 676 '(26 * 26)
    If col3 > 0 Then A1(i * 2) = col3 + 64: i = i + 1
    col2 = (col / 26) - col3 * 26
    If col2 > 0 Then A1(i * 2) = col2 + 64: i = i + 1
    col1 = col Mod 26
    A1(i * 2) = col1 + 64
R1C1toA1 = A1
End Function
Изменено: bedvit - 25.05.2021 09:50:13
«Бритва Оккама» или «Принцип Калашникова»?
 
Наконец-то нормальная дискуссия  :D
bedvit, не хватает закрывающей кавычки - пишет "A <> "A" + не понял, нафига в функции номер строки, убрал (и нет - выдаёт неправильно НЕ из-за того, что я убрал)
Код
Изменено: Jack Famous - 25.05.2021 11:20:24
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Код
x = Split(Columns(4).Address(0, 0), ":")(0)
Или я не о том? )
 
Юрий М, #1 строка кода 67 - о том, но я это уже оттестировал и оно самое медленное  :)
Прятал под спойлерами, чтобы оформить красиво и место не занимать попусту, а получается, что уже как минимум второй (DANIKOLA ещё) коды не смотрел  :D
Изменено: Jack Famous - 25.05.2021 11:50:29
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Я показал методику. Хорошо, сделал все за тебя :)
1 млн. определений 3х букв столбца - 0,7 сек.
Код
Sub testC1toA1()
Dim t, x
t = Timer
For x = 1 To 1000000
C1toA1 (16000)
Next
Debug.Print "C1toA1 " & Timer - t
End Sub

Function C1toA1(col As Long) As String 
Dim A1() As Byte, i As Long, col3 As Long, col2 As Long, col1 As Long
    i = 0
    col3 = col \ 676 '(26 * 26)
    If col3 > 0 Then ReDim A1(0 To 5): A1(i * 2) = col3 + 64: i = i + 1
    col2 = (col \ 26) - col3 * 26
    If col2 > 0 Then
        If i = 0 Then ReDim A1(0 To 3)
        A1(i * 2) = col2 + 64
        i = i + 1
    End If
    col1 = col Mod 26
    If i = 0 Then ReDim A1(0 To 1)
    A1(i * 2) = col1 + 64
C1toA1 = A1
End Function
«Бритва Оккама» или «Принцип Калашникова»?
 
Я смотрел, но не увидел.
А вообще количество спойлеров в твоих сообщениях впечатляет )
 
bedvit, почти - попробуй передать №26 (Z)
Процедура проверки корректности функции-кандидата
Цитата
Юрий М: количество спойлеров в твоих сообщениях впечатляет
удобно - можно прятать длинные тексты, картинки и коды
Все бы так делали, меньше скролить пришлось бы  :D
За мастер-класс, кстати, спасибо Виктору - это он меня как-то научил  :idea:
Изменено: Jack Famous - 25.05.2021 12:23:15
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Немного подправил, но умещаюсь в 1 сек на 1 млн определений 3х букв столбца
Код
Sub testC1toA1()
    Dim t, x
    t = Timer
    For x = 1 To 1000000
    C1toA1 (16000)
    Next
    Debug.Print "C1toA1 " & Timer - t
End Sub

Function C1toA1(col As Long) As String
Dim A1() As Byte, i As Long, col3 As Long, col2 As Long, col1 As Long, col4 As Long, cQUO As Long, cMOD As Long
    i = 0
    cQUO = (col - 26) \ 676 '(26 * 26)
    cQUO2 = (col - 26) \ 26
    cMOD = col Mod 26
    cMOD2 = cQUO2 Mod 26
    If cMOD = 0 And cMOD2 = 0 Then
        If cQUO > 0 Then col3 = cQUO - 1 Else col3 = cQUO
    Else
        col3 = cQUO
    End If
    If col3 > 0 Then ReDim A1(0 To 5): A1(i * 2) = col3 + 64: i = i + 1
    If cMOD = 0 Then col2 = (col \ 26) - col3 * 26 - 1 Else col2 = (col \ 26) - col3 * 26
    If col2 > 0 Then
        If i = 0 Then ReDim A1(0 To 3)
        A1(i * 2) = col2 + 64
        i = i + 1
    End If
    If cMOD = 0 Then col1 = 26 Else col1 = cMOD
    If i = 0 Then ReDim A1(0 To 1)
    A1(i * 2) = col1 + 64

    C1toA1 = A1
End Function
«Бритва Оккама» или «Принцип Калашникова»?
 
Думаю можно еще оптимизнуть, но нет времени, может кто-то из спецов доведет до ума :)
«Бритва Оккама» или «Принцип Калашникова»?
 
bedvit, достойно и на 3ёх буквах, как и в цикле по всем номерам столбцов (потому что большинство столбцов именно трёхбуквенные) безусловный лидер, но в целом это последнее место среди участников, тем более, что нормальные данные редко в столбцах после 702го (703 = AAA) располагаются
Скрин
Очень хорошо — спасибо за отличный вариант!  :idea:

Если даже совместить (в лоб) Recur - для столбцов 1-702 и BedVit'a — для 703 и далее, то будет хуже (Combine), чем простая рекурсия за счёт дополнительных проверок
Изменено: Jack Famous - 25.05.2021 18:09:46
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Немного ускорил, протестируй пожалуйста
Код
Function C1toA1(col As Long) As Byte()  'номер столбца в букву
Dim A1() As Byte, cQUO As Long, cMOD As Long, cQUO2 As Long, cMOD2 As Long
    If col <= 26 Then ' одна буква
        ReDim A1(0 To 1)
        A1(0) = col + 64
    ElseIf col <= 702 Then ' 2 буквы
        ReDim A1(0 To 3)
        cQUO = col \ 26
        cMOD = col Mod 26
        If cMOD = 0 Then cQUO = cQUO - 1: cMOD = 26
        A1(0) = cQUO + 64
        A1(2) = cMOD + 64
    Else ' 3 буквы
        ReDim A1(0 To 5)
        cQUO = col \ 26
        cMOD = col Mod 26
        cQUO2 = (col - 26) \ 676 '(26 * 26)
        cMOD2 = (col - 26) Mod 676
        If cMOD2 = 0 Then cQUO2 = cQUO2 - 1
        If cMOD = 0 Then cQUO = cQUO - 1: cMOD = 26
        A1(0) = cQUO2 + 64
        A1(2) = (cQUO - cQUO2 * 26) + 64
        A1(4) = cMOD + 64
    End If
    C1toA1 = A1
End Function
«Бритва Оккама» или «Принцип Калашникова»?
 
bedvit,
Готово. 1 буква очень долго
ща ускорю — есть идея  :)

Ну вот - теперь топ (хотя проигрыш на первых двух всё-равно есть и это плохо)
изменил возврат функции на String, чтобы ускорить получение буквы для однобуквенных столбцов  8)

Минорные изменения и сравнение 2ух лидеров (BedVit and Recur)
Выводы:
Честно говоря, на таких скоростях, уже без разницы, какую из этих двух функций использовать, т.к. разницу пользователь вряд ли заметит  :D
К тому же - данная функция у меня является лишь частью общей задачи, а значит её время работы будет лишь частью общего времени, а значит будет ещё менее заметно

Отдельное спасибо bedvit'у — единственному, кто составил конкуренцию представленным функциям
Тесты проведены, результаты описаны и сохранены на Планете — это главное :idea:
Изменено: Jack Famous - 26.05.2021 14:42:54
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Здравствуйте, Алексей! Спасибо за очередную интересную тему! В качестве послесловия:  :)
Код
Function Newbie(ByVal n As Long) As String
  Static init As Long, a(1 To 16384) As String
  Dim i As Long
  If init = 0 Then
    For i = 1 To UBound(a)
      a(i) = Recur(i)
    Next i
    init = 1
  End If
  Newbie = a(n)
End Function
Владимир
 
sokol92, приветствую и спасибо ВАМ за такую оценку, Владимир!  :oops:
Ничего себе "послесловие"  8-0  :idea:
Это ж ГЕНИАЛЬНО! В такие моменты думаешь, "почему Я до этого не додумался"  :D
А ведь я тестил статику, но на словарях - а нафига словари, если по индексу массива можно всяко быстрее доставать без всякого поиска  - балда я  :D
Вот только не пойму, почему вариант от Виталия стал так сильно тормозить — не было такого…
Отчёт (конкурентов нет)
ща попробую счётчик на флаг заменить и сравнить на бОльшем цикле

Отчёт
не понимаю, почему даже на миллионе вызовов 26ти однобуквенных столбцов, вычисляемая функция Chr$() выигрывает у получения элемента из массива по индексу  8-0
Я буду использовать вариант от Владимира и ещё раз зарублю себе про мощь статики  :idea:
Изменено: Jack Famous - 27.05.2021 13:12:24
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Ого, Владимир - респект!
Раз такая пьянка пошла, сделал некоторые улучшения и в своей функции, тестируйте :)
Код
Function C1toA1_v2(col As Long) As String 'номер столбца в букву
Dim cQUO As Long, cMOD As Long, cQUO2 As Long, cMOD2 As Long
    If col <= 26 Then ' одна буква
        C1toA1_v2 = Chr$(col + 64)
    ElseIf col <= 702 Then ' 2 буквы
        cQUO = col \ 26
        cMOD = col Mod 26
        If cMOD = 0 Then cQUO = cQUO - 1: cMOD = 26
        C1toA1_v2 = Chr$(cQUO + 64) & Chr$(cMOD + 64)
    Else ' 3 буквы
        cQUO = col \ 26
        cMOD = col Mod 26
        cQUO2 = (col - 26) \ 676 '(26 * 26)
        cMOD2 = (col - 26) Mod 676
        If cMOD2 = 0 Then cQUO2 = cQUO2 - 1
        If cMOD = 0 Then cQUO = cQUO - 1: cMOD = 26
        C1toA1_v2 = Chr$(cQUO2 + 64) & Chr$((cQUO - cQUO2 * 26) + 64) & Chr$(cMOD + 64)
    End If
End Function
«Бритва Оккама» или «Принцип Калашникова»?
 
bedvit, да блин — только к твоему суперсловарю вернулся  :D Ща  8)
Рекурсия побеждена
Поздравляю, Виталь, и с возвращением  :D Сейчас объединю твой вариант со статикой и можно расходится, я думаю  :D Скорость просто фантастическая!  :idea:

Финальный отчёт
Изменено: Jack Famous - 27.05.2021 14:09:02
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1
Наверх