Объединяйте повторяющиеся элементы и суммируйте значения — в одной строке/ячейке

У меня есть эта строка в одной ячейке (A1):

Apple – 1
Banana – 6
Cherry – 10
Peach – 100
Apple – 1000
Kiwi – 10999
Peach – 44
Fig – 3/100
Fig – 3/100

Мне нужно найти формулу, которая сможет объединять повторяющиеся записи и суммировать их значения. Итак, желаемый результат таков (также в одной ячейке, скажем, B1):

Apple – 1001
Banana – 6
Cherry – 10
Peach – 144
Kiwi – 10999
Fig – 6/100

Я пробовал это часами, но не могу понять, как это сделать. Я попробовал сначала найти уникальные значения. Пока проблем нет:

=UNIQUE(
    ARRAYFORMULA(
        REGEXEXTRACT(
            TRANSPOSE(SPLIT(A1, CHAR(10))),
            "(.*) – [0-9.]+/?[0-9]?"
        )
    )
)

возвращает массив уникальных значений

{"Apple", "Banana", "Cherry", "Peach", "Kiwi", "Fig"}

Лучший результат, который я смог придумать, — это формула для работы с Apple и Banana вручную:

=ARRAYFORMULA(
    {
        "Apple" & " – " & SUM(
            ARRAYFORMULA(
                IF(
                    REGEXMATCH(
                        TRANSPOSE(SPLIT(A1, CHAR(10))),
                        "Apple" & " – [0-9.]+/?[0-9]?"
                    ),
                    VALUE(
                        REGEXEXTRACT(
                            TRANSPOSE(SPLIT(A1, CHAR(10))),
                            "Apple – ([0-9.]+)/?[0-9]?"
                        )
                    ),
                    ""
                )
            )
        );
        "Banana" & " – " & SUM(
            ARRAYFORMULA(
                IF(
                    REGEXMATCH(
                        TRANSPOSE(SPLIT(A1, CHAR(10))),
                        "Banana" & " – [0-9.]+/?[0-9]?"
                    ),
                    VALUE(
                        REGEXEXTRACT(
                            TRANSPOSE(SPLIT(A1, CHAR(10))),
                            "Banana – ([0-9.]+)/?[0-9]?"
                        )
                    ),
                    ""
                )
            )
        )
    }
)

это правильно выводит массив

{"Apple – 1001", "Banana – 6"}

Но как бы я ни старался, мне не удается заставить его работать динамически для всех уникальных элементов. Мне кажется, что я разгадал обе части головоломки, но просто не могу сложить их вместе.

So the desired output is this: Это тоже в одной камере?
Tedinoz 28.02.2024 01:42

желательно да. однако преобразовать его из массива в одну ячейку должно быть довольно просто (что-то вроде TEXTJOIN(CHAR(10), TRUE, [array here])), поэтому, если вы сможете понять, как составить формулу массива, этого, вероятно, тоже будет достаточно.

user2015253 28.02.2024 01:48

Извините за путаницу. Я не предлагал решения, я просто пытался прояснить вопрос. Было бы полезно, если бы вы отредактировали вопрос, чтобы было ясно, что предлагаемый результат должен содержаться в одной ячейке.

Tedinoz 28.02.2024 02:00

да, да, сделаю!

user2015253 28.02.2024 02:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
4
756
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вот возможное решение:

=ARRAYFORMULA(
  LET(data,WRAPROWS(SUBSTITUTE(SPLIT(SUBSTITUTE(A1,"/","ζ")," –"&CHAR(10)),"ζ","/"),2),
      items,INDEX(data,,1),
      qty,MAP(INDEX(data,,2),LAMBDA(n,SORTN(QUERY(,"select "&n)))),
      JOIN(CHAR(10),MAP(UNIQUE(items),LAMBDA(item,item&" – "&SUM((item=items)*qty))))))

Массив data форматирует данные так, чтобы мы могли с ними работать:

WRAPROWS(SUBSTITUTE(SPLIT(SUBSTITUTE(A1,"/","ζ")," –"&CHAR(10)),"ζ","/"),2)

Массив qty преобразует числа в их недробное представление:

MAP(INDEX(data,,2),LAMBDA(n,SORTN(QUERY(,"select "&n))))

Затем мы перебираем каждый уникальный фрукт, используя функцию MAP:

MAP(UNIQUE(items),LAMBDA(item,

И для каждого фрукта мы выполняем условную сумму, где диапазон критериев — это массив всех фруктов, критерий — текущий фрукт, а диапазон сумм — это массив величин, связанных с каждым фруктом:

SUM((item=items)*qty)

Наконец, соединяем все вместе:

JOIN(CHAR(10),...)

Альтернативные представления дробных величин

Уменьшенные фракции

=ARRAYFORMULA(
  LET(data,WRAPROWS(SUBSTITUTE(SPLIT(SUBSTITUTE(A1,"/","ζ")," –"&CHAR(10)),"ζ","/"),2),
      items,INDEX(data,,1),
      qty,INDEX(data,,2),
      JOIN(CHAR(10),
        MAP(UNIQUE(items),LAMBDA(item,    
          LET(qty_,FILTER(qty,items=item),
              s,SPLIT(TOCOL(REGEXREPLACE(qty_,"^[^/]+$","$0/1")),"/"),
              nums,INDEX(s,,1),dens,INDEX(s,,2),l,LCM(dens),nnum,SUM(l/dens*nums),g,GCD(nnum,l),
              item&" – "&IF(COUNTIF(qty_,"*/*")=0,SUM(--qty_),nnum/g&"/"&l/g)))))))

Нередуцированные дроби

=ARRAYFORMULA(
  LET(data,WRAPROWS(SUBSTITUTE(SPLIT(SUBSTITUTE(A1,"/","ζ")," –"&CHAR(10)),"ζ","/"),2),
      items,INDEX(data,,1),
      qty,INDEX(data,,2),
      JOIN(CHAR(10),
        MAP(UNIQUE(items),LAMBDA(item,    
          LET(qty_,FILTER(qty,items=item),
              s,SPLIT(TOCOL(REGEXREPLACE(qty_,"^[^/]+$","$0/1")),"/"),
              nums,INDEX(s,,1),dens,INDEX(s,,2),l,LCM(dens),
              item&" – "&IF(COUNTIF(qty_,"*/*")=0,SUM(--qty_),SUM(l/dens*nums)&"/"&l)))))))

Не работает, когда для фрукта есть как дробные, так и не дробные части. Например, введите первую запись «Яблоко — 1/4», и формула не даст правильный результат. (Я очень ценю ответ. Хотя я не могу проголосовать за него прямо сейчас, поскольку не проверил правильный ответ, я хотел бы поучиться на вашем подходе и должен вернуться, чтобы проголосовать за него позже.)

Argyll 28.02.2024 07:39

Позвольте мне сформулировать: проблема не в смешивании дробей и недробий. Проблема в том, что числа, выглядящие как дроби, можно интерпретировать как дату (серийный номер) (Google Sheet).

Argyll 28.02.2024 08:28

Удивительный! Однако, похоже, возникает проблема, когда мы добавляем в меню элементы, содержащие пробелы, например, если мы добавляем Apple Pie – 8 в меню. Мне удалось решить эту проблему, удалив пробел из параметра SPLIT и добавив вместо него TRIM: WRAPROWS(TRIM(SUBSTITUTE(SPLIT(SUBSTITUTE(B2,"/","ζ"),"–"&CH‌​AR(10)),"ζ","/")),2)

user2015253 28.02.2024 13:07

Сначала разделите данные на правильный двумерный массив, а затем используйте запрос для группировки данных:

=ARRAYFORMULA(QUERY(
  SPLIT(TOCOL(SPLIT(A1,CHAR(10))),"– "),
  "Select Col1,sum(Col2) group by Col1",0)
)

Отсюда,

  • Дроби не преобразуются в числа и, следовательно, не суммируются по запросу. Функция «Оценка» недоступна в таблицах Google, но вы сможете использовать мой трюк с запросом, чтобы преобразовать дроби в реальные числа.

  • Данные не объединяются в одну ячейку. Использование BYROW/TEXTJOIN должно быть тривиальным.

  • Десятичные дроби можно преобразовать обратно в дроби с помощью TEXT

=ARRAYFORMULA(
   LET(splitData, SPLIT(TOCOL(SPLIT(A1,CHAR(10))),"– "),
       numData,MAP(splitData,LAMBDA(a,IFERROR(--query(,"Select "&a&" label "&a&" ''",0),a))),
       groupedData, QUERY(numData,
          "Select Col1,sum(Col2) group by Col1 label sum(Col2) ''",0
         ),
       rowJointData, BYROW(groupedData, LAMBDA(r, JOIN("-", TEXT(r,"# #/100")))),
       TEXTJOIN(CHAR(10),,rowJointData)
      )
)

Это решение предназначено для имитации «цикла» по входному массиву (однократно).

Чтобы выполнить вышеизложенное, мы используем reduce для хранения совокупного одномерного массива формата {name1,val1,name2,val2,...} при прохождении входного массива. Конечно, нам нужно сначала использовать tocol(split(..., чтобы преобразовать одну входную ячейку в массив. Нам также потребуются промежуточные шаги для определения совпадающих имен и обработки значений, чтобы они могли sum легко это сделать.

В конце концов, мы можем обработать окончательный агрегат во что угодно. В приведенном ниже примере я использовал makearray, чтобы преобразовать однострочный массив в массив n-by-1 с помощью concat, как будто сначала преобразую в массив n-by-2, а затем concat по столбцам. Здесь makearray представляет собой нечто общее, что вы всегда можете заставить работать. Любое конкретное преобразование может иметь свой собственный, более простой или более эффективный подход.

Идея проста. Хотя код будет длинным, поскольку в нем много вызовов hstack, index(...,i,j) и choosecols(...,1,sequence(... для доступа и хранения элементов массива. Они создают много беспорядка. Надеюсь, расстояние ниже поможет прояснить ход вещей.


Сначала создайте именованную функцию для обработки потенциальных дробей следующим образом:

let(sepByVal,split(x,"/"),if (columns(sepByVal)>1,index(sepByVal,1,1)/index(sepByVal,1,2),sepByVal))

Вы можете обновить эту функцию, если форматирование ввода изменится.

Или, если вам хочется сохранить целочисленную дробь (в виде текста), именованная функция — это то, что вам нужно.


Затем в B1 следующая формула даст желаемый результат.

=let(sep,"–"
    ,input,tocol(split(A1,char(10)))
    ,arr,reduce(hstack(index(regexextract(index(input,1,1),"[A-Za-z]+"),1,1),0)
               ,input
               ,lambda(a,c
                      ,let(sepByVal,regexextract(c,"([A-Za-z]+)[ "&sep&"]*([0-9]+[./0-9]*)")
                          ,ind,iferror(match(index(sepByVal,1,1),a,0),0)
                          ,if (ind
                             ,hstack(choosecols(a,sequence(1,ind))
                                    ,index(a,1,ind+1)
                                     +FRACTION2NUM(index(sepByVal,1,2))
                                    ,if (ind<columns(a)-2
                                     ,choosecols(a,sequence(1,columns(a)-ind-1,ind+2))
                                       ,tocol(,3)))
                             ,hstack(a,index(sepByVal,1,1)
                                 ,FRACTION2NUM(index(sepByVal,1,2)))))))
    ,join(char(10),makearray(columns(arr)/2,1,lambda(i,j,index(arr,1,2*i-1)&" "&sep&" "&index(arr,1,2*i)))))

Обязательно скопируйте символ прямо из введенного вами текста.

Замена последней строки на

,out,wraprows(arr,2)
,join(char(10),arrayformula(index(out,,1)&" "&sep&" "&index(out,,2))))

может сделать формулу более знакомой.

Приведенная выше формула была намеренно создана для совместимости с вводом массива. Вы можете заменить определение input соответствующим образом, если у вас есть входной массив. (Как показано на изображении ниже.)

Но если у вас есть входной массив, обязательно отметьте пустое внутри lambda, в основном, если оно пустое, то передайте a, иначе следуйте остальной логике (или используйте filter снаружи reduce). Таким образом, вы можете работать с бесконечными диапазонами.

Наконец, совет о том, как составить такую ​​формулу самостоятельно: при использовании reduce (в столбце) используйте горизонтальный массив в качестве агрегата. Если вы столкнулись с какой-либо ошибкой, измените диапазон ввода на A$1:A1 и перетащите его вниз. После этого вы визуально увидите промежуточные агрегаты. В результате любую ошибку будет легко обнаружить. Именно поэтому лучше сначала преобразовать одну ячейку в массив и создать формулы, которые работают с входными данными массива.


Немного философских мыслей... Почему makepivottable не является встроенной функцией...? Почему reshape (как представление, если вывод используется в строке) не является встроенной функцией...? Почему set(val,arr,i,j) не является встроенной функцией...?

Формула в B1:

=INDEX(JOIN(CHAR(10),BYROW(LET(x,SPLIT(TOCOL(SPLIT(REGEXREPLACE(A1,"/\d+",),CHAR(10)))," – ",0),u,UNIQUE(INDEX(x,,1)),HSTACK(u,MAP(u,LAMBDA(y,SUM(FILTER(INDEX(x,,2),INDEX(x,,1)=y))&IFERROR(REGEXEXTRACT(A1,y&" – \d+(/\d+)"),))))),LAMBDA(r,JOIN(" – ",r)))))

Это предполагает, что входные данные последовательны, и суммы в ваших данных имеют один и тот же тип; таким образом, либо целые числа, либо части одного и того же порядка!

Однако, если вашей целью было суммировать все виды значений (включая различные типы арифметических операторов), попробуйте следующее:

=INDEX(JOIN(CHAR(10),BYROW(LET(x,SPLIT(TOCOL(SPLIT(A1,CHAR(10)))," – ",0),QUERY({INDEX(x,,1),MAP(INDEX(x,,2),LAMBDA(y,QUERY(,"select "&y&" label "&y&" ''")))},"Select Col1, Sum(Col2) Group By Col1 Label Sum(Col2) ''")),LAMBDA(r,JOIN(" – ",r)))))

Другие вопросы по теме

Похожие вопросы

Множественный поиск и замена с включениями и исключениями
Как мне получить сумму чьих-либо доходов на нескольких таблицах, где она будет суммироваться только в том случае, если указано имя этого человека в таблицах Google
Ошибка 503 в боковой панели и модальном окне скрипта Google Apps
Всплывающее окно при редактировании ячейки – Google Sheet
Используйте раскрывающийся список (из диапазона) для ввода данных. Изменить элементы раскрывающегося списка. Данные устарели (как отразить изменения?)
Загрузка файлов, созданных Google Apps Script (Sheets), через Ruby
Google Таблицы: используйте условную функцию, чтобы разделить предложение в одной ячейке на несколько частей в последующих ячейках
Google Таблицы: разбейте текст на группы по четыре слова: поместите каждую группу из четырех слов в ячейки по всей строке, максимум 12 слов / 3 столбца
Как получить всю информацию о листах Google в определенном диапазоне? (Текст и стиль)
Google Таблицы группируются по строкам?