Доброго времени суток, Планетяне! Подскажите, пожалуйста, — как можно автоматизировать сортировку по шифрам, представляющих собой тексто-численные данные (см. скрин и пример)
В примере, числа между шифром и рангом - просто демонстрация, что я по ним сортировал. Задача сводится к получению РАНГА или аналога, по которому можно провести корректную сортировку таблицы.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
wowick, ну это вариант "в лоб" и только для конкретного примера. Если уж использовать доп. столбцы, то можно заменять нецифровые символы (33-47 и 58-255) пробелами, потом "сжать" и "разбить" по пробелу, получив массив цифр для доп. столбцов. Спасибо за пример.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Доброе время суток. Вариант на регулярных выражениях, создать столбец для сортировки, приводя все числа внутри к одному формату 0000.
Скрытый текст
Код
Public Sub test()
Dim pSheet As Worksheet, tmp As String
Dim LRow As Long, vData As Variant
Dim pReg As Object, i As Long, pItem As Object
Set pSheet = ActiveSheet
LRow = pSheet.Cells(pSheet.Rows.Count, 1).End(xlUp).Row
vData = pSheet.Range(pSheet.Cells(1, 1), pSheet.Cells(LRow, 1)).Value
vData(1, 1) = "order"
Set pReg = CreateObject("VBScript.RegExp")
pReg.Global = True: pReg.Pattern = "\d+"
For i = 2 To UBound(vData)
tmp = pReg.Replace(vData(i, 1), "|||")
For Each pItem In pReg.Execute(vData(i, 1))
tmp = Replace$(tmp, "|||", Format$(CLng(pItem.Value), "0000"), 1, 1)
Next
vData(i, 1) = tmp
Next
pSheet.Range("A1").End(xlToRight).Offset(0, 1).Resize(UBound(vData), 1).Value = vData
End Sub
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Андрей VG, фигасе)))) какое интересное решение))) спасибо большое! Всё работает!)) Я так понимаю, что к количеству чисел в шифре решение не привязано - верно?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: что к количеству чисел в шифре решение не привязано
Да, правильно. Под числом подразумевается последовательность, состоящая только из цифр, количеством от одной и более. Чуть переделал, с учётом, что вы пользуетесь умными таблицами.
Скрытый текст
Код
Public Sub makeCodeSortable()
Const codeColIdx = 1 'номер столбца умной таблицы с шифрами
Const numFormat = "0000" 'числовой формат для замены
Dim pSheet As Worksheet, tmp As String, pLCol As ListColumn
Dim vData As Variant, pLO As ListObject
Dim pReg As Object, i As Long, pItem As Object
Set pSheet = ActiveSheet
Set pLO = pSheet.ListObjects(1)
vData = pLO.ListColumns(codeColIdx).DataBodyRange.Value
Set pReg = CreateObject("VBScript.RegExp")
pReg.Global = True: pReg.Pattern = "\d+"
For i = 1 To UBound(vData)
tmp = pReg.Replace(vData(i, 1), "|||")
For Each pItem In pReg.Execute(vData(i, 1))
tmp = Replace$(tmp, "|||", Format$(CLng(pItem.Value), numFormat), 1, 1)
Next
vData(i, 1) = tmp
Next
Set pLCol = pLO.ListColumns.Add(pLO.ListColumns.Count + 1)
pLCol.DataBodyRange.Value = vData
End Sub
Андрей VG, вот спасибо, мил человек!)) сейчас пишу UDF, вдохновлённый вашим решением (но по-своему пишу). Ваш макрос намного быстрее будет (буду использовать в комплексных макросах), а UDF применять удобно для разовых случаев (создал столбец, ввёл функцию, отсортировал по столбцу, удалил столбец)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Андрей VG, прошу прокомментировать - немного не понимаю…
Код
Public Sub makeCodeSortable()
Const codeColIdx = 1 'номер столбца умной таблицы с шифрами
Const numFormat = "0000" 'числовой формат для замены
Dim pSheet As Worksheet, tmp As String, pLCol As ListColumn
Dim vData As Variant, pLO As ListObject
Dim pReg As Object, i As Long, pItem As Object
Set pSheet = ActiveSheet
Set pLO = pSheet.ListObjects(1)
vData = pLO.ListColumns(codeColIdx).DataBodyRange.Value 'забираем в массив столбец шифров
Set pReg = CreateObject("VBScript.RegExp")
pReg.Global = True: pReg.Pattern = "\d+" 'отбираем числа (цифра одна или несколько, идущие подряд)
For i = 1 To UBound(vData) 'для каждой ячейки с шифром
tmp = pReg.Replace(vData(i, 1), "|||") 'вот тут не очень ясно (временный текст=во всём словаре заменить значение текущей ячейки на "|||"???)
For Each pItem In pReg.Execute(vData(i, 1)) 'для каждого из найденных (в ячейке шифра) чисел
tmp = Replace$(tmp, "|||", Format$(CLng(pItem.Value), numFormat), 1, 1) 'меняем во временном тексте "|||" на найденное число в текстовом формате с ведущими нулями (первое найденное и одно вхождение)
Next
vData(i, 1) = tmp 'пишем в массив готовую текстовую строку
Next
Set pLCol = pLO.ListColumns.Add(pLO.ListColumns.Count + 1) 'создаём столбец в конце таблицы
pLCol.DataBodyRange.Value = vData 'выгружаем массив в созданный столбец
End Sub
проблема с 15 строкой (как мне кажется). Учитывая результат, в ней вы меняете найденные по шаблону числа на "|||", чтобы потом это "|||" заменить уже на числа в текстовом формате с ведущими нулями… Сначала не понял, почему только первое и одно вхождение меняете, но потом стало ясно, что чисел несколько и вы их меняете по очереди (от первого и до последнего) и при следующем "прогоне" первым найденным будет уже следующее "|||", которое заменится на очередное число
UPD (про 15 строку кода): так и есть [ССЫЛКА] — tmp = pReg.Replace(vData(i, 1), "|||") означает, что не вся ячейка (данные массива) меняется на "|||" (как мне показалось). Это означает, что найденное по шаблону (числа), меняются на временную строку "|||", чтобы впоследствии эту строку обратно заменить на числа, но уже в нужном формате. Надо снова учить матчасть)) Если что - поправьте) спасибо большое ещё раз!!!
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
ГОТОВО! Выкладываю дополненный вариант кода от Андрея. Теперь можно оставить только числа через пробел и сортировать по ним. Решил не заморачиваться с функцией, ведь так намного проще и быстрее — спасибо за науку!
SortableCode_VG
Код
Option Explicit
'===================================================================================================================
'Приведение чисел, смешанных с текстом в формат для сортировки (вариант для "умных" таблиц)
'Принцип: создаёт справа от столбца с шифром столбец, который заполняется шифрами, отформатированными для корректной сортировки
'Варианты работы: 1. форматировать только числа в шифре 2. оставить только отформатированные числа в шифре
'Автор: Андрей VG
'Дополнения: Jack Famous
'Тема на форуме: https://www.planetaexcel.ru/forum/index.php?PAGE_NAME=read&FID=1&TID=101942&TITLE_SEO=101942-korrektnaya-sortirovka-ranzhirovanie-chisel-smeshannykh-s-tekstom
'===================================================================================================================
Sub SortableCode_VG()
Const numFormat = "0000" 'числовой формат для замены
Dim pRegNum As Object, pRegNoNum As Object, pItem As Object
Dim pLO As ListObject, pLCol As ListColumn
Dim vData
Dim OnlyNum As Boolean
Dim choose As Byte
Dim col%
Dim i&
Dim tmp$, str$
On Error GoTo ex
col = Application.InputBox("Укажите любую ячейку столбца ШИФРА:", "Запрос данных.", Selection.Address, Type:=8)(1).Column
choose = MsgBox("Оставить только числа?", vbYesNoCancel + vbQuestion + vbDefaultButton2)
Select Case choose
Case vbYes: OnlyNum = True
Case vbNo: OnlyNum = False
Case vbCancel: GoTo ex
End Select
Application.ScreenUpdating = 0
On Error GoTo er
Set pLO = ActiveSheet.ListObjects(1)
vData = pLO.ListColumns(col).DataBodyRange.Value 'забираем в массив столбец шифров
Set pRegNum = CreateObject("VBScript.RegExp")
pRegNum.Global = True: pRegNum.Pattern = "\d+" 'отбираем числа
Set pRegNoNum = CreateObject("VBScript.RegExp")
pRegNoNum.Global = True: pRegNoNum.Pattern = "[^0-9]" 'отбираем НЕчисла
str = "|||"
For i = 1 To UBound(vData) 'для каждой ячейки с шифром
tmp = pRegNum.Replace(vData(i, 1), str) 'заменяем найденные числа на временную строку
For Each pItem In pRegNum.Execute(vData(i, 1)) 'для каждого из найденных (в ячейке шифра) чисел…
tmp = Replace$(tmp, str, Format$(CLng(pItem.Value), numFormat), 1, 1) 'меняем во временную строку обратно на найденное число, но уже в текстовом формате с ведущими нулями (первое найденное и одно вхождение)
Next pItem
If OnlyNum = True Then tmp = pRegNoNum.Replace(tmp, " "): tmp = Application.WorksheetFunction.Trim(tmp)
vData(i, 1) = CStr(tmp) 'пишем в массив готовую текстовую строку
Next i
Set pLCol = pLO.ListColumns.Add(col + 1) 'создаём столбец справа от столбца шифров
pLCol.DataBodyRange.Value = vData 'выгружаем массив в созданный столбец
GoTo fin
ex:
MsgBox "Отмена выполнения…", vbInformation, "EXIT"
GoTo fin
er:
MsgBox "КРИТИЧЕСКАЯ ОШИБКА!", vbCritical, "FATAL ERROR"
fin:
On Error GoTo 0
Application.ScreenUpdating = 1
End Sub
Пока писал код, вспомнил, что этот метод "забивания нулями" для сортировки где-то уже видел (кажется, Казанский демонстрировал в какой-то теме) и даже писал, что это очень круто, однако, башка дырявая - вот и забыл
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, Вот еще вариантик, там если "ав" меняется, то тоже учитывается и доп. столбцы не нужны ". |—|—| (|)" - все что слева или справа от "|" считается разделителем
Код
Function CodeConvert(mask, aCode, dlmtr As String)
Dim aMask, aVal(), x, n As Long, m As Long, i As Long, j As Long, dlmtr As String
If Left(mask, "1") = dlmtr Then mask = Right(mask, Len(mask) - 1)
If Right(mask, "1") = dlmtr Then mask = Left(mask, Len(mask) - 1)
aMask = Split(mask, dlmtr)
ReDim aVal(LBound(aCode) To UBound(aCode), 0 To UBound(aMask) + 1)
i = LBound(aCode)
For Each x In aCode
n = 1
For j = 0 To UBound(aMask)
m = InStr(n, x, aMask(j), 1)
If m > 0 Then
If Len(x) >= n Then
aVal(i, j) = Mid(x, n, m - n): n = m + Len(aMask(j))
If IsNumeric(aVal(i, j)) Then aVal(i, j) = CDbl(aVal(i, j))
End If
End If
Next
If n <= Len(x) Then
aVal(i, j) = Right(x, Len(x) - n + 1)
If IsNumeric(aVal(i, j)) Then aVal(i, j) = CDbl(aVal(i, j))
End If
i = i + 1
Next
CodeConvert = aVal
End Function
Sub Test()
Dim aVal(), a(), a0() As Long, rngData As Range, i As Long, j As Long, aResult()
Set rngData = Sheets("sort").Cells(1).CurrentRegion
With rngData.Offset(1).Resize(rngData.Rows.Count - 1)
a = .Resize(, 1).Value
aVal = CodeConvert(". |—|—| (|)", a, "|")
ReDim aColumn(LBound(aVal, 2) To UBound(aVal, 2))
For i = LBound(aVal, 2) To UBound(aVal, 2)
aColumn(i) = i
Next
Call SortAll(aVal, aColumn, a0)
a = .Value: ReDim aResult(LBound(a) To UBound(a), LBound(a, 2) To UBound(a, 2))
For i = LBound(a) To UBound(a)
For j = LBound(a, 2) To UBound(a, 2): aResult(i, j) = a(a0(i), j): Next
Next
Sheets("result").Range(.Address) = aResult
End With
End Sub
AAF, спасибо) некорректно работает при увеличении количества чисел и изменении разделителя
может я неправильно как-то объяснил, но, разумеется меня интересует не "узкий" вариант под конкретный шаблон шифра (в таком случае можно было совсем простую функцию написать), а решение, позволяющее бОльшую часть число-текстовых данных корректно сортировать.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
AAF, ну вот смотрите - мне каждый раз в код лезть, чтобы эти паттерны менять? Кроме того вообще отдельный лист нужен для результатов. Задача же не в том, чтобы отсортировать внутри кода, а получить РАНГ, по которому потом исходные данные можно отсортировать. Столбец шифра ведь лишь один из нескольких столбцов таблицы. Чтобы быстро и корректно отсортировать таблицу по шифру, я не могу придумать, как тут поможет ваше решение…
Решение от Андрея, крайне универсально и со всем этим прекрасно справляется (гляньте). Кажется мы немного о разном говорим…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
AAF, исправил пост выше. Найденное решение является макросом. Нет - не обязательно функцией. Сначала я именно о ней думал, но, потестив макрос Андрея понял, что макрос намного удобнее…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Я понял... Просто пример такой сделал. У меня там промежуточный массив выдает CodeConvert. Вот из него можно и ранг сделать. Другой вопрос в том, если не только числовые значения, но текстовые участки могут меняться...?
AAF, ну вот я взял код Андрея, добавил возможность выводить только числа - в таком варианте сортировать можно учитывая только числа и их последовательность. А в классическом варианте, текст при сортировке будет учитываться - просто все числа нулями забиваются до одинаковой разрядности
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Да, понял, просто нужен ранг. Я говорил о том, что если "ав" может быть и "аг", а еще чего доброго просто "а". Ну это наверно выходит за пределы задачи. Но если нет, то там тоже нужно задавать формат, типа "0000". Я просто хотел предложить сортировать вообще без ранга.... и без указания форматов. Хотя и там могут быть ограничения. Ведь все зависит от философии формирования кода. И здесь абсолютно универсального способа нет.
AAF, вы смотрели это? Просто вы пишете ровно то, что там реализовано и учтено. Как по мне - крайне универсально (вопрос необходимости и достаточности, как и всегда)
Цитата
AAF написал: предложить сортировать вообще без ранга
повторю - смысл не в том, чтобы получить отсортированный диапазон, а в том, чтобы отсортировать ИСХОДНИК, находящийся в таблице))) В примере из шапки видно, что я "выдёргиваю" числа из шифра в отдельные столбцы и сортирую по ним. Решение Андрея даёт абсолютно тот же результат, только без необходимости плодить столбцы (достаточно одного). Можно, конечно ещё автоматизировать, сортируя таблицу по временному столбцу макросом и удаляя его после этого, но мне больше так нравится - визуальный контроль не помешает))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Anchoret, посмотрите пожалуйста сообщение №11 и скажите, что там не так с кодом (может я проглядел что-то) если интересно, то предложите альтернативный вариант решения - я люблю расширять кругозор, но не всегда могу понять))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
вот хрен знает - то ли я объясняю не так, то ли что)))) не нужен мне сортировщик (ну то есть это необязательно). Нужно решение, чтобы можно было сортировать шифры более "корректно". Одно решение найдено и отлично работает (и сортировки в коде вообще нет) - просто интересно, как можно по-другому было бы решить…
Ну нет так нет - что ж такого)) спасибо за внимание к теме
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, ради любопытства замерьте таймером обработку от 100к до 1кк через RegExp. Я не проверял, собственно вообще пока им не пользовался. Если будет долго, то стоит искать обходные пути.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Anchoret, в общем, если ручками, то при таком монструозном примере Replace вызывает Overflow. Сработал только на 1к ячеек примерно…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄