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 очень неплоха, надо будет воспользоваться, когда совсем грустно бдует, эта безграмотная ботяра ничего путевого не напишет, если не было готового решения.

UFO landed and left these words here

Следующий конкурс Сбера: напиши 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