Pull to refresh

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)
            }
        }

Что, если данные нужно сначала вытащить из базы и их довольно много? Чистый sql?

Интересно было бы посоревноваться. А идея самоутвердиться за счет GPT очень неплоха, надо будет воспользоваться, когда совсем грустно бдует, эта безграмотная ботяра ничего путевого не напишет, если не было готового решения.

Сейчас в него добавят все текущие решения и победить его уже будет сложнее

Следующий конкурс Сбера: напиши 30 строк кода на итераторах из стандартной библиотеки Rust и выиграй дворец в Геленджике :)

А бункер выдадут и бронепоезд впридачу?

Странно. Решение победиля, работает за квадрат, когда как решение друга работает за линию. Но короче, да. На сложность жюри было наплевать?


Я не специалист по жаве, но наверняка есть похожее лакончиное решение, где сначала строится Map для GetFeatures, где по categoryId храниться список всех фич уже отфильтрованных, а потом строка, как в решении победителя, только не надо каждый раз вызвать GetFeatures() и фильтровать для каждой категории.

Моя знакомая запостила решение для JS. Мы вместе думали над кривым ТЗ, а фидбека она так никакого и не получила.

Весьма корявый какой-то конкурс получился.

Ну там вроде про красиво, а не про быстро. А решение друга не отличается ни красивостью, ни простотой восприятия.

В описании конкурса есть "Чистый, изящный, лаконичный, читаемый и понятный код, который работает без багов".
Тормозящее на пустом месте решение — можно считать за баг. Неужели никто не нашелся, кто написал похожее лаконичное решение но без тормозов? Что-то типа того, что Viacheslav01 выше написал через groupBy.


Edit:
Ахаха. Только сейчас заметил, что вам ответил ссылкой на ваш же код.

Насчёт читаемый и понятный у победителя всё плохо. Я котлина не знаю, но код автора понять могу, код победителя нет.

Тормозящее на пустом месте решение

"Ерунда - еще один сервер поднимем и будет норм, а в конечные требования допишем еще + 4 гига оперативы. Давай в продакшн".

П.С. //sarcasm off

Там по жаве было смешнее. Нужно было написать сервер, который принимает строку рест запросом, и проверяет правильность скобок.

Что по мне, правильное решение проверять это на фронте и на слать эту хрень на сервер.

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

Так мы ничего не делаем, мы клиенты возвращаем тру или фалс, а он дальше сам делает что хочет.

Я бы сгорел, если б у меня такие бэкендеры были. Бэкенд - и для апки, и для сайта, и для другой апки. Ну вы поняли.

Может всё-таки общую работу на бэк выносить? Тем более, что производительность клиента гораздо критичнее на пользователя действует.

Ну и логика тоже максимально на сервере должна обрабатываться.

А валидацию там телефона, или что поле логин не пустое, или что в возрасте только цифры и галочка в графе пол есть. Тоже на бэк? Вы здоровый ваще?

UFO landed and left these words here

Такие "нормальные человеки" — обычно причина всяких sql-инъекций и прочей радости.

UFO landed and left these words here

sql-инъекция — прямое следствие отсутствия валидации данных, пришедших с клиента.

UFO landed and left these words here

Ну так задача чтоб проверить запрос и вернуть его. Ладно бы дальше что-то делать. Но тут просто вернуть тру или 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 котлин я тоже не знаю

Зная ответ, я тоже понимаю этот код) смысл в том, чтоб понимать его неё зная ответа))

Но вот *, и totypedarray, что делают мне непонятно,не зная ответа)

Вообще самый читаемый код, это где сверху коммент есть с объяснением, что он делает, будем честны.

Автору поста было бы полезно прочитать книгу "Чистый код".

Я бы сказал, что изначальная задача в принципе крайне топорная. Почему нужно возвращать плоский список в таком формате и заставлять потребителя 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
) }
}

Поспешил с предыдущим комментарием, поправил, в итоге:

fun getResultList() = 
      getFeatures()
        .groupBy { it.categoryId }
        .let { getCategories()
                 .flatMap { category ->
                            listOf(category,
                                   *it[category.categoryId]?
                                      .toTypedArray()?:emptyArray(),
                                    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
)

Sign up to leave a comment.

Articles