Comments 45
Т.е. в принципе сложность не имела значения? Все что увидел это M*N, хотя можно и за M+N.
Как то так, не уверен, что работает)))
fun getResultList() =
buildList<Any> {
val features = getFeatures().groupBy { it.categoryId }
getCategories().forEach { category ->
add(category)
features[category.categoryId]?.let(::addAll)
add(category.categoryId)
}
}
Интересно было бы посоревноваться. А идея самоутвердиться за счет GPT очень неплоха, надо будет воспользоваться, когда совсем грустно бдует, эта безграмотная ботяра ничего путевого не напишет, если не было готового решения.
Следующий конкурс Сбера: напиши 30 строк кода на итераторах из стандартной библиотеки Rust и выиграй дворец в Геленджике :)
Странно. Решение победиля, работает за квадрат, когда как решение друга работает за линию. Но короче, да. На сложность жюри было наплевать?
Я не специалист по жаве, но наверняка есть похожее лакончиное решение, где сначала строится Map для GetFeatures, где по categoryId храниться список всех фич уже отфильтрованных, а потом строка, как в решении победителя, только не надо каждый раз вызвать GetFeatures() и фильтровать для каждой категории.
Моя знакомая запостила решение для JS. Мы вместе думали над кривым ТЗ, а фидбека она так никакого и не получила.
Ну там вроде про красиво, а не про быстро. А решение друга не отличается ни красивостью, ни простотой восприятия.
В описании конкурса есть "Чистый, изящный, лаконичный, читаемый и понятный код, который работает без багов".
Тормозящее на пустом месте решение — можно считать за баг. Неужели никто не нашелся, кто написал похожее лаконичное решение но без тормозов? Что-то типа того, что Viacheslav01 выше написал через groupBy.
Edit:
Ахаха. Только сейчас заметил, что вам ответил ссылкой на ваш же код.
Там по жаве было смешнее. Нужно было написать сервер, который принимает строку рест запросом, и проверяет правильность скобок.
Что по мне, правильное решение проверять это на фронте и на слать эту хрень на сервер.
Ну такто сервер должен убедиться, что ему прислали валидный запрос, а не мусор, прежде чем начать его выполнять.
@dididididi
никогда не доверяй клиенту =)
Даже строже: любые внешние данные - не верны. ;)
Так мы ничего не делаем, мы клиенты возвращаем тру или фалс, а он дальше сам делает что хочет.
Я бы сгорел, если б у меня такие бэкендеры были. Бэкенд - и для апки, и для сайта, и для другой апки. Ну вы поняли.
Может всё-таки общую работу на бэк выносить? Тем более, что производительность клиента гораздо критичнее на пользователя действует.
Ну и логика тоже максимально на сервере должна обрабатываться.
Ну так задача чтоб проверить запрос и вернуть его. Ладно бы дальше что-то делать. Но тут просто вернуть тру или false надо было
Если в рамках чисто этой задачке, то мы ничего не делаем с запросом. Мы просто возвращаем тру или фалс. А клиент может ваще ответ заигнорить
Ну если давать задание писать полноценный сервер, который делает то да се, то там любое решение будет в колобайты кода. Это не так просто проверять, да и писать сильно дольше. В соревнованиях дают ограниченные мелкие задачки обычно.
Вот, кстати, как проверять читаемость кода? По идее берём 10 джунов и просим объяснить что этот код делает. Смотрим, сколько поняли и ставим оценку равную этому числу.
Поэтому весь код со странными, редкими, непонятными функциями сразу в пролёте, а код пишется на циклах и ифчиках.
Стримы и функциональщину многие не понимают и не любят.
А если джунов учить стримам и функциональщине, а не императивщине, то решение победителя они поймут, а автора - нет. Вообще, может в жава-мире это не распространено, но во многих других языках работа с коллекциями через map/filter/reduce и другие подобные штуки чуть ли не по-умолчанию уже.
В жаве тоже распространено. И тоже по умолчанию.
Но жава - это ООП и учить функциональщину странно.
Это вопрос про методики оценки читаемости и понятности. Мы берем десять случайных джунов и показываем им код. И если они первый понимают, а второй нет, то второй кол автоматом идёт нафиг, как нечитаемый.
Мы не знаем какие джуны, чему их учили и как. Просто случайная выборка. Я сам не знаю результатов, мне интересно было бы узнать.
Но жава - это ООП и учить функциональщину странно.
Сейчас почти все языки - смесь ООП и функциональщины. И Java старается не отставать.
Не, можно конечно писать в старом-добром стиле Java 1.5, но зачем люди вводят все эти functional interface и прочие плюшки? Тем более, им уже куча лет, на самом деле.
В жаве тоже распространено. И тоже по умолчанию.
Тогда какие проблемы с пониманием кода могут быть? Вы, например, писали, что код не поняли. Но это же буквально условие задачи, записанное в виде кода
fun getResultList() =
getCategories().flatMap { category -> // для каждой категории
listOf( // составляем список, в котором
// Первый элемент связан с категорией (Category). Хранит в себе всю информацию о категории.
category,
// Далее идут все элементы, связанные с характеристикой (Feature) относящиеся к данной категории.
*getFeatures().filter { it.categoryId == category.categoryId }.toTypedArray(),
// После последней характеристики, относящийся к открытой категории, идет элемент, сигнализирующий о том, что категория закончилась. Хранит в себе только CategoryId.
category.categoryId
)
} // ну и flatmap для создание плоского массива
Читаемо, лаконично, понятно (но долго по времени выполнения, да).
А вот полотна кода автора сложны, потому что длинные. Тут даже не нужно особо знать сигнатуры функций, из названий flatMap и listOf всё понятно. Или это настолько редкие функции, что их никто из джунов (и видимо не только джунов) не знает? Я правда не могу понять
Ps котлин я тоже не знаю
Автору поста было бы полезно прочитать книгу "Чистый код".
Я бы сказал, что изначальная задача в принципе крайне топорная. Почему нужно возвращать плоский список в таком формате и заставлять потребителя API обрабатывать отклонения от него, если можно вернуть сразу Map<Int, Pair<String, Feature>>
? Это сразу позволит получить имя категории по её ID и список соответствующих фич за O(1), что выглядит более полезной вещью, чем список с линейным лукапом, а перевод в плоскую итерацию может быть сделан и на вызывающей стороне, причём без материализации списка.
Участвовал по категории Java. Отправил значит заявку с кодом - кнопка прожалась и никакой обратной связи, никаких уведомлений на почту или телегу.
После даты результатов из оферты захожу на сайт - ничего про результаты, в поисковиках тоже ноль. Только HR написал из Сбера мол : вы участвовали в конкурсе, не рассматриваете ли предложени - я понял, что заявка таки дошла. Но на вопрос по результатам он предложил обратиться к организаторам, хорошо что не в отделение )
Всегда думал, что на такого рода конкурсах задачки уровня "hard", если использовать градацию Литкода. А тут, кажется, от силы "easy". Захотелось на скорую руку прикинуть задачку на typescript. Немножко увлекся, и получилось чуть больше, чем "на скорую руку". Жалко выбрасывать, когда можно не выбрасывать, поэтому решил поделиться своим текущим видением "красоты" и выложить, вдруг какому-нибудь начинающему пригодится. Да и продвинутым тоже может пригодиться - чтобы поупражняться в критике.
// combine.ts
type CategoryId = number
type FeatureId = number
export type Category = {
categoryId: CategoryId
name: string
}
export type Feature = {
featureId: FeatureId
categoryId: CategoryId
title: string
value: number
}
// m+n space complexity
// m+n time complexity
export function combine(categories: Category[], features: Feature[]) {
const categoryMap = new Map(categories.map((c) => [c.categoryId, c]))
const featureMap: Map<CategoryId, Set<Feature>> = new Map()
// "features" may contain a non-existent category in "categories",
// so you need to check it out
for (const feature of features) {
if (categoryMap.has(feature.categoryId)) {
const featureSet = featureMap.get(feature.categoryId) || new Set()
featureSet.add(feature)
featureMap.set(feature.categoryId, featureSet)
} // else -> found a feature with categoryId that isn't present in "categories"
}
const combined: (Category | Feature | CategoryId)[] = []
for (const [categoryId, featureSet] of featureMap.entries()) {
combined.push(categoryMap.get(categoryId) as Category)
combined.push(...featureSet)
combined.push(categoryId)
}
return combined
}
Протестируем ф-ию combine, хотя бы и на одном примере:
// test.ts
import type { Category, Feature } from "./combine"
import { combine } from "./combine"
const C0 = { categoryId: 0, name: "category-0" }
const C1 = { categoryId: 1, name: "category-1" }
const C_WITHOUT_FEATURES = { categoryId: 2, name: "category-2" }
const Categories: Category[] = [C0, C1, C_WITHOUT_FEATURES]
const F00 = {
featureId: 0,
categoryId: 0,
title: "feature-0-category-0",
value: 0,
}
const F10 = {
featureId: 1,
categoryId: 0,
title: "feature-1-category-0",
value: 10,
}
const F21 = {
featureId: 2,
categoryId: 1,
title: "feature-2-category-1",
value: 21,
}
const F31 = {
featureId: 3,
categoryId: 1,
title: "feature-3-category-1",
value: 32,
}
const F_WITHOUT_CATEGORY = {
featureId: 4,
categoryId: 1000,
title: "feature-4-category-1000",
value: 4000,
}
const Features: Feature[] = [F00, F10, F21, F31, F_WITHOUT_CATEGORY]
const Combined = [C0, F00, F10, C0.categoryId, C1, F21, F31, C1.categoryId]
// "naive" implementation, but it's enough for the moment
function arraysAreEqual(arr1: any[], arr2: any[]) {
if (arr1.length !== arr2.length) return false
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) return false
}
return true
}
function test() {
const ok = arraysAreEqual(Combined, combine(Categories, Features))
console.log(ok ? "Ok" : "Fail")
}
test()
Вроде бы работает.
А вот и (само)критика подоспела:
function arraysAreEqual(arr1: any[], arr2: any[]) {
if (arr1.length !== arr2.length) return false
return arr1.every((a, i) => a === arr2[i])
}
Продолжаю сеанс саморазоблачения. Можно было вместо
combined.push(categoryMap.get(categoryId) as Category)
сombined.push(...featureSet)
combined.push(categoryId)
написать
combined.push(
categoryMap.get(categoryId) as Category)
...featureSet,
categoryId
)
Да тут как только нельзя, и красивее и компактнее, включая использование генераторов, если немного посидеть над этим... Понятно, что во время конкурса время и нервы, и особо размусоливать некогда... Ну зато хоть осознал всю глубину и пагубность своей затяжной усталости. Этим и оправдываюсь, если что)
Напоследок родилось вот это:
// m+n space complexity
// m+n time complexity
export function group(categories: Category[], features: Feature[]) {
const groupped: Map<CategoryId, [Feature[], Category]> = new Map(
categories.map((c) => [c.categoryId, [[], c]])
)
features.forEach((f) => {
if (groupped.has(f.categoryId)) {
const [features_, _] = groupped.get(f.categoryId)
features_.push(f)
}
})
return [...groupped.entries()].reduce(
(acc, [catId, [features_, category]]) => {
if (features_.length > 0) {
acc.push(category, ...features_, catId)
}
return acc
},
[]
)
}
На чем и прекращаю конвульсии
Решение в итоге красивое. Попробовал чуть оптимизировать производительность:
fun getResultList() = getFeatures().associateBy { it.categoryId }
.let {
getCategories().flatMap { category ->
listOf( category,
it[category.categoryId],
category.categoryId
) }
}
data class CategoryOrFeatureOrEndElement(
val categoryId: Int,
val categoryName: String? = null,
val featureId: Int ? = null,
val featureTitle: String? = null,
var featureValue: Int ? = null
)
// Вместо
data class CategoryOrFeatureOrEndElement(
val categoryId: Int,
val categoryName: String? = null,
val featureId: Int? = null,
val featureTitle: String? = null,
var featureValue: Int? = null
)
Как я пытался писать красивый код