Страницы: 1
RSS
Как расписать построчно наименования, которые в списке заданы тремя точками и перечислением
 
Здравствуйте.
Помогите решить вопрос с формулой.
У меня в диапазоне C5:E14 есть список позиций, который имеет свои номера записанные через запятую с пробелом, а некоторые через тройную точку.
Как формулой, без дополнительных столбцов - разложить эти номера позиций - по строчкам ?
(С учетом того, что то что записано через тройную точку - нужно расписать как полный список).
Изменено: visors16 - 26.06.2026 08:54:01
 
Если прям без дополнительных столбцов, то можно так:
Код
=ЗНАЧЕН(СЖПРОБЕЛЫ(ПСТР(ПОДСТАВИТЬ(ПОДСТАВИТЬ(ПОДСТАВИТЬ(ПОДСТАВИТЬ(ПОДСТАВИТЬ(ПОДСТАВИТЬ($C$6&", "&$C$7&", "&$C$8&", "&$C$9&", "&$C$10&", "&$C$11&", "&$C$12&", "&$C$13&", "&$C$14;"5...7";"5, 6, 7");"9...15";"9, 10, 11, 12, 13, 14, 15");"21. ..25";"21, 22, 23, 24, 25");"28...31";"28, 29, 30, 31");"32...34";"32, 33, 34");", ";ПОВТОР(" ";2^8));(2^8)*(СТРОКА(L1)-1)+1;2^8)))
Волшебные числа можно заменить на все комбинации чисел. Их не больше 600 штук(100 додвузначных чисел * на максимальную разницу 6(строка "9...15")).
 
А так можно подтянуть наименование позиции.
Код
=ЕСЛИОШИБКА(ИНДЕКС($D$6:$D$14;ЕСЛИОШИБКА(ЕСЛИОШИБКА(ПОИСКПОЗ(L18&", *";$C$6:$C$14;0);ПОИСКПОЗ(L18&".*";$C$6:$C$14;0));ПОИСКПОЗ(L18&", *";$C$6:$C$14;0)));O17)
 
Вариант через пользовательскую функцию. Без дополнительных столбцов и волшебных чисел. Можно вводить как формулу массива в диапазон, или как обычную формулу, в этом случае указываем индекс элемента.
Код
Option Explicit
' =============================================================================
' Функция: РАСПИСАТЬ
' Описание: Раскрывает сокращённые записи номеров (например, "1...5" в "1, 2, 3, 4, 5")
' Параметры:
'   - номера: диапазон ячеек с номерами
'   - строка: опционально - индекс для возврата конкретного элемента
' Возвращает: массив номеров или конкретный элемент по индексу
' =============================================================================
Function РАСПИСАТЬ(номера As Range, Optional строка As Long) As Variant
    Dim numbers As Variant
    numbers = CLngArray(GetOneSizeArray(EditNumbersArray(GetNumbersArray(GetValueFromRange(номера)))))
    If строка > LBound(numbers, 1) Then
        If строка <= UBound(numbers, 1) Then
            РАСПИСАТЬ = numbers(строка, 1)
        End If
    Else
        РАСПИСАТЬ = numbers
    End If
End Function

Private Function CLngArray(arr As Variant) As Variant
    Dim ya As Long
    For ya = LBound(arr, 1) To UBound(arr, 1)
        If IsNumeric(arr(ya, 1)) Then
            arr(ya, 1) = CLng(arr(ya, 1))
        End If
    Next
    CLngArray = arr
End Function

Private Function GetOneSizeArray(ByVal arr As Variant) As Variant
    Dim ya As Long, brr As Variant, yb As Long, va As Variant
    For ya = LBound(arr) To UBound(arr)
        arr(ya) = Split(arr(ya), ", ")
        yb = yb + (UBound(arr(ya)) - LBound(arr(ya)) + 1)
    Next
    If yb = 0 Then Exit Function
    ReDim brr(1 To yb, 1 To 1)
    yb = 0
    For ya = LBound(arr) To UBound(arr)
        For Each va In arr(ya)
            yb = yb + 1
            brr(yb, 1) = va
        Next
    Next
    GetOneSizeArray = brr
End Function

Private Function EditNumbersArray(ByVal arr As Variant) As Variant
    Dim ya As Long
    For ya = LBound(arr) To UBound(arr)
        If InStr(arr(ya), ". ..") > 0 Then
            arr(ya) = Replace(arr(ya), ". ..", "...")
        End If
        If InStr(arr(ya), "...") > 0 Then
            arr(ya) = SplitString(arr(ya))
        End If
    Next
    EditNumbersArray = arr
End Function

Private Function SplitString(ByVal source As String) As String
    Dim arr As Variant, ya As Long
    arr = Split(source, ", ")
    For ya = LBound(arr) To UBound(arr)
        If InStr(arr(ya), "...") > 0 Then
            arr(ya) = SplitValue(arr(ya))
        End If
    Next
    SplitString = Join(arr, ", ")
End Function

Private Function SplitValue(ByVal source As String) As String
    Dim arr As Variant, ya As Long
    arr = Split(source, "...")
    
    Dim iMin As Long, iMax As Long, flagNumeric As Boolean
    If IsNumeric(arr(0)) And IsNumeric(arr(1)) Then
        iMin = CLng(arr(0))
        iMax = CLng(arr(1))
        If iMin > iMax Then
            iMin = iMax
            iMax = CLng(arr(0))
        End If
        flagNumeric = True
    Else
        iMin = Asc(arr(0))
        iMax = Asc(arr(1))
        If iMin > iMax Then
            iMin = iMax
            iMax = Asc(arr(0))
        End If
    End If
    
    Dim brr As Variant, yb As Long
    ReDim brr(iMin To iMax) As String
    If flagNumeric Then
        For yb = iMin To iMax
            brr(yb) = yb
        Next
    Else
        For yb = iMin To iMax
            brr(yb) = Chr(yb)
        Next
    End If
    SplitValue = Join(brr, ", ")
End Function

Private Function GetNumbersArray(arr As Variant) As Variant
    Dim yb As Long, ya As Long, xa As Long
    ReDim brr(1 To UBound(arr, 1) * UBound(arr, 2))
    For xa = 1 To UBound(arr, 2)
        For ya = 1 To UBound(arr, 1)
            If arr(ya, xa) <> "" Then
                yb = yb + 1
                brr(yb) = arr(ya, xa)
            End If
        Next
    Next
    If yb = 0 Then Exit Function
    If yb < UBound(brr) Then ReDim Preserve brr(LBound(brr) To yb)
    GetNumbersArray = brr
End Function

Private Function GetValueFromRange(rr As Range) As Variant
    Set rr = Intersect(rr, rr.Parent.UsedRange)
    
    Dim arr As Variant
    If rr.Cells.CountLarge = 1 Then
        ReDim arr(1 To 1, 1 To 1)
        arr(1, 1) = rr.Value
    Else
        arr = rr.Value
    End If
    GetValueFromRange = arr
End Function

 
Вариант с пользовательской функцией, выводящей наименования.
Код
Option Explicit
' =============================================================================
' Функция: РАСПИСАТЬ
' Описание: Раскрывает сокращённые записи номеров (например, "1...5" в "1, 2, 3, 4, 5")
' Параметры:
'   - номера: диапазон ячеек с номерами
'   - строка: опционально - индекс для возврата конкретного элемента
' Возвращает: массив номеров или конкретный элемент по индексу
' =============================================================================
Function РАСПИСАТЬ(номера As Range, Optional строка As Long) As Variant
    Dim numbers As Variant
    numbers = CLngArray(GetOneSizeArray(EditNumbersArray(GetNumbersArray(GetValueFromRange(номера), Empty)), 0))
    If строка >= LBound(numbers, 1) Then
        If строка <= UBound(numbers, 1) Then
            РАСПИСАТЬ = numbers(строка, 1)
        End If
    Else
        РАСПИСАТЬ = numbers
    End If
End Function
' =============================================================================
' Функция: РАСПИСАТЬНАИМЕНОВАНИЯ
' Описание: Раскрывает сокращённые записи номеров и связывает их с наименованиями
' Параметры:
'   - номера: диапазон ячеек с номерами
'   - наименования: диапазон ячеек с наименованиями (должен совпадать по размеру)
'   - строка: опционально - индекс для возврата конкретной строки
' Возвращает: двумерный массив {номер, наименование} или конкретная строка
' =============================================================================
Function РАСПИСАТЬНАИМЕНОВАНИЯ(номера As Range, наименования As Range, Optional строка As Long) As Variant
    Set наименования = наименования.Cells(1, 1).Resize(номера.Rows.Count, номера.Columns.Count)
    Dim numbers As Variant
    numbers = GetOneSizeArray(EditNumbersArray(GetNumbersArray(GetValueFromRange(номера), GetValueFromRange(наименования))), 1)
    If строка >= LBound(numbers, 1) Then
        If строка <= UBound(numbers, 1) Then
            РАСПИСАТЬНАИМЕНОВАНИЯ = numbers(строка, 1)
        End If
    Else
        РАСПИСАТЬНАИМЕНОВАНИЯ = numbers
    End If
End Function

Private Function CLngArray(arr As Variant) As Variant
    Dim ya As Long
    For ya = LBound(arr, 1) To UBound(arr, 1)
        If IsNumeric(arr(ya, 1)) Then
            arr(ya, 1) = CLng(arr(ya, 1))
        End If
    Next
    CLngArray = arr
End Function

Private Function GetOneSizeArray(ByVal arr As Variant, xa As Long) As Variant
    Dim ya As Long, brr As Variant, yb As Long, va As Variant
    For ya = LBound(arr) To UBound(arr)
        arr(ya)(0) = Split(arr(ya)(0), ", ")
        yb = yb + (UBound(arr(ya)(0)) - LBound(arr(ya)(0)) + 1)
    Next
    If yb = 0 Then Exit Function
    ReDim brr(1 To yb, 1 To 1)
    yb = 0
    For ya = LBound(arr) To UBound(arr)
        For Each va In arr(ya)(0)
            yb = yb + 1
            If xa = 0 Then
                brr(yb, 1) = va
            Else
                brr(yb, 1) = arr(ya)(xa)
            End If
        Next
    Next
    GetOneSizeArray = brr
End Function

Private Function EditNumbersArray(ByVal arr As Variant) As Variant
    Dim ya As Long
    For ya = LBound(arr) To UBound(arr)
        If InStr(arr(ya)(0), ". ..") > 0 Then
            arr(ya)(0) = Replace(arr(ya)(0), ". ..", "...")
        End If
        If InStr(arr(ya)(0), "...") > 0 Then
            arr(ya)(0) = SplitString(arr(ya)(0))
        End If
    Next
    EditNumbersArray = arr
End Function

Private Function SplitString(ByVal source As String) As String
    Dim arr As Variant, ya As Long
    arr = Split(source, ", ")
    For ya = LBound(arr) To UBound(arr)
        If InStr(arr(ya), "...") > 0 Then
            arr(ya) = SplitValue(arr(ya))
        End If
    Next
    SplitString = Join(arr, ", ")
End Function

Private Function SplitValue(ByVal source As String) As String
    Dim arr As Variant, ya As Long
    arr = Split(source, "...")
    
    Dim iMin As Long, iMax As Long, flagNumeric As Boolean
    If IsNumeric(arr(0)) And IsNumeric(arr(1)) Then
        iMin = CLng(arr(0))
        iMax = CLng(arr(1))
        If iMin > iMax Then
            iMin = iMax
            iMax = CLng(arr(0))
        End If
        flagNumeric = True
    Else
        iMin = Asc(arr(0))
        iMax = Asc(arr(1))
        If iMin > iMax Then
            iMin = iMax
            iMax = Asc(arr(0))
        End If
    End If
    
    Dim brr As Variant, yb As Long
    ReDim brr(iMin To iMax) As String
    If flagNumeric Then
        For yb = iMin To iMax
            brr(yb) = yb
        Next
    Else
        For yb = iMin To iMax
            brr(yb) = Chr(yb)
        Next
    End If
    SplitValue = Join(brr, ", ")
End Function

Private Function GetNumbersArray(arr As Variant, nrr As Variant) As Variant
    If IsEmpty(nrr) Then ReDim nrr(1 To UBound(arr, 1), 1 To UBound(arr, 2))
    
    Dim yb As Long, ya As Long, xa As Long
    ReDim brr(1 To UBound(arr, 1) * UBound(arr, 2))
    For xa = 1 To UBound(arr, 2)
        For ya = 1 To UBound(arr, 1)
            If arr(ya, xa) <> "" Then
                yb = yb + 1
                brr(yb) = Array(arr(ya, xa), nrr(ya, xa))
            End If
        Next
    Next
    If yb = 0 Then Exit Function
    If yb < UBound(brr) Then ReDim Preserve brr(LBound(brr) To yb)
    GetNumbersArray = brr
End Function

Private Function GetValueFromRange(rr As Range) As Variant
    Set rr = Intersect(rr, rr.Parent.UsedRange)
    
    Dim arr As Variant
    If rr.Cells.CountLarge = 1 Then
        ReDim arr(1 To 1, 1 To 1)
        arr(1, 1) = rr.Value
    Else
        arr = rr.Value
    End If
    GetValueFromRange = arr
End Function

 
Отрефакторенный вариант.
Скрытый текст
 
pq
 
pqm
Пришелец-прораб.
 
Цитата
МатросНаЗебре написал:
Если прям без дополнительных столбцов, то можно так:
Спасибо. Нормально.
Изменено: visors16 - 27.06.2026 02:57:06
 
sotnikov, AlienSx, это же макросы все.
А я про формулы спрашивал.
Макрос же не будет работать в формате xlsx...
 
Это не макросы - это Power Query, и оно прекрасно работает в файлах *.xlsx
Согласие есть продукт при полном непротивлении сторон
 
Цитата
visors16:   про формулы спрашивал
д.массив
 
.
Изменено: visors16 - 30.06.2026 18:40:21
 
visors16, ну не первый год на форуме. Картинки не вставляйте в сообщение напрямую
Согласие есть продукт при полном непротивлении сторон
 
.
Изменено: visors16 - 30.06.2026 18:39:03
 
Цитата
visors16 написал: У меня показывает вот такую ошибку
Очень информативно)
Файл с ошибкой приложите
Согласие есть продукт при полном непротивлении сторон
 
.
Изменено: visors16 - 30.06.2026 18:39:14
 
Ну, я думаю, и ПавелW и МатросНаЗебре проверяли работу своих формул/макросов. И файлы у них приложены. А у Вас только картинки
Согласие есть продукт при полном непротивлении сторон
 
 .
Изменено: visors16 - 30.06.2026 18:39:31
 
ПавелW, разобрался с этой формулой.
Спасибо.
Изменено: visors16 - 30.06.2026 18:40:36
 
Цитата
visors16 написал:
Макрос же не будет работать в формате xlsx...
- будет. И в виде UDF тоже будет.
Макрос только не может в таких файлах сохраняться, но никто не отменяет надстройки, или другие открытые файлы с этими макросами.
 
Любопытно было бы видеть решение по извлечению всех чисел одной формулой для старых офисов.
 
memo, какое отношение Ваш вопрос имеет к теме топика?
Согласие есть продукт при полном непротивлении сторон
 
Sanja, Не совсем понял, вопрос.
Цитата
visors16 написал:
Как формулой, без дополнительных столбцов - разложить эти номера позиций - по строчкам ? (С учетом того, что то что записано через тройную точку - нужно расписать как полный список).
Меня интересует существует ли решение для старых версий Excel. Формулы от МатросНаЗебре, если что видел.
 
memo, и я не понял Ваш вопрос
В теме не ИЗВЛЕКАЮТ все числа откуда-то, а ... ну Вы поняли)
Согласие есть продукт при полном непротивлении сторон
 
Цитата
memo:  Любопытно было бы видеть решение ... одной формулой для старых офисов.
memo, впринципе решение из  #12 можно адаптировать для "старых версий"
буквально этого не стоит делать)) , а вот переменные из LET определить в Дисптчер имен...
далее, ПОСЛЕД() преобразовать в соответствующий массив из СТРОКА(...
АГРЕГАТ() заменить на НАИМЕНЬШИЙ()
есть еще небольшие нюансы...
но, думаю, вам вполне всё это по силам ;-)
 
ПавелW,
Вы меня раскусили  :D
Я надеялся на все готовенькое.
Да, там есть нюансы, они простые на первый взгляд но очень важные. Это как с атомной бомбой, схемы везде нарисованны, принцип известен, а как собрать знают единицы.
 
memo, пожалуйста)
честно говоря, я думал у вас есть другой алгоритм решения)
...можно, кстати, задействовать ФИЛЬТР.XML(), но не думаю что будет сильно короче, потом эта функция несколько тормознутая и со своими ограничениями
Цитата
как с атомной бомбой ...собрать знают единицы
ну, стоит отметить что нет такой "единицы") , т.е. проектирование и сборка требуют междисциплинарной команды высококлассных специалистов из совершенно разных областей науки и промышленности
но "посыл" ясен - главное "в нужном месте стукнуть молоточком" чтоб заработало)
))
 
Цитата
ПавелW написал:
можно, кстати, задействовать ФИЛЬТР.XML(),
Увы, я прикинул и отказался от этой идеи, поскольку формула выходила не трехэтажная, а размером с небоскреб. Разумеется, именные диапазоны я при этом не рассматривал,
Создать массив чисел несложно, определить отсутствующие числа и сконвертировать их в массив тоже. Отделить мух от котлет, то бишь отдельные числа от разделенных точками, да еще и в нужной последовательности - через 7-8 этажей, в принципе можно. НО, если бы в диапазоне присутствовал только один интервал разделенный точками, то это слегка облегчило бы дело, но их два, а по факту могут быть несколько. Их все надо последовательно обработать.
Выйду в отпуск и может быть, чисто ради интереса, сделаю, но ни о каком практическом применении тут и речи быть не может)
Изменено: memo - 04.07.2026 15:51:33
Страницы: 1
Читают тему
Наверх