https://github.com/abhi18av/innovation-competition
Submission for a programming challenge
https://github.com/abhi18av/innovation-competition
clojure clojurescript data-analysis
Last synced: 6 days ago
JSON representation
Submission for a programming challenge
- Host: GitHub
- URL: https://github.com/abhi18av/innovation-competition
- Owner: abhi18av
- License: epl-2.0
- Created: 2019-08-20T10:09:44.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2019-09-02T10:58:50.000Z (almost 7 years ago)
- Last Synced: 2025-02-23T18:33:59.480Z (over 1 year ago)
- Topics: clojure, clojurescript, data-analysis
- Language: HTML
- Homepage:
- Size: 2.32 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.html
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
README.md
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
body {
font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
font-size: 14px;
padding: 0 12px;
line-height: 22px;
word-wrap: break-word;
}
#code-csp-warning {
position: fixed;
top: 0;
right: 0;
color: white;
margin: 16px;
text-align: center;
font-size: 12px;
font-family: sans-serif;
background-color:#444444;
cursor: pointer;
padding: 6px;
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}
#code-csp-warning:hover {
text-decoration: none;
background-color:#007acc;
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}
body.scrollBeyondLastLine {
margin-bottom: calc(100vh - 22px);
}
body.showEditorSelection .code-line {
position: relative;
}
body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
content: "";
display: block;
position: absolute;
top: 0;
left: -12px;
height: 100%;
}
body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
left: -30px;
}
.vscode-light.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(0, 0, 0, 0.15);
}
.vscode-light.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(0, 0, 0, 0.40);
}
.vscode-dark.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.vscode-dark.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 255, 255, 0.60);
}
.vscode-high-contrast.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 160, 0, 0.7);
}
.vscode-high-contrast.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 160, 0, 1);
}
img {
max-width: 100%;
max-height: 100%;
}
a {
color: #4080D0;
text-decoration: none;
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
}
h1, h2, h3 {
font-weight: normal;
}
h1 code,
h2 code,
h3 code,
h4 code,
h5 code,
h6 code {
font-size: inherit;
line-height: auto;
}
a:hover {
color: #4080D0;
text-decoration: underline;
}
table {
border-collapse: collapse;
}
table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
padding: 5px 10px;
}
table > tbody > tr + tr > td {
border-top: 1px solid;
}
blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left: 5px solid;
}
code {
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
font-size: 14px;
line-height: 19px;
}
body.wordWrap pre {
white-space: pre-wrap;
}
.mac code {
font-size: 12px;
line-height: 18px;
}
pre:not(.hljs),
pre.hljs code > div {
padding: 16px;
border-radius: 3px;
overflow: auto;
}
/** Theming */
.vscode-light,
.vscode-light pre code {
color: rgb(30, 30, 30);
}
.vscode-dark,
.vscode-dark pre code {
color: #DDD;
}
.vscode-high-contrast,
.vscode-high-contrast pre code {
color: white;
}
.vscode-light code {
color: #A31515;
}
.vscode-dark code {
color: #D7BA7D;
}
.vscode-light pre:not(.hljs),
.vscode-light code > div {
background-color: rgba(220, 220, 220, 0.4);
}
.vscode-dark pre:not(.hljs),
.vscode-dark code > div {
background-color: rgba(10, 10, 10, 0.4);
}
.vscode-high-contrast pre:not(.hljs),
.vscode-high-contrast code > div {
background-color: rgb(0, 0, 0);
}
.vscode-high-contrast h1 {
border-color: rgb(0, 0, 0);
}
.vscode-light table > thead > tr > th {
border-color: rgba(0, 0, 0, 0.69);
}
.vscode-dark table > thead > tr > th {
border-color: rgba(255, 255, 255, 0.69);
}
.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
border-color: rgba(0, 0, 0, 0.18);
}
.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
border-color: rgba(255, 255, 255, 0.18);
}
.vscode-light blockquote,
.vscode-dark blockquote {
background: rgba(127, 127, 127, 0.1);
border-color: rgba(0, 122, 204, 0.5);
}
.vscode-high-contrast blockquote {
background: transparent;
border-color: #fff;
}
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Tomorrow Comment */
.hljs-comment,
.hljs-quote {
color: #8e908c;
}
/* Tomorrow Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
color: #c82829;
}
/* Tomorrow Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
color: #f5871f;
}
/* Tomorrow Yellow */
.hljs-attribute {
color: #eab700;
}
/* Tomorrow Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
color: #718c00;
}
/* Tomorrow Blue */
.hljs-title,
.hljs-section {
color: #4271ae;
}
/* Tomorrow Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #8959a8;
}
.hljs {
display: block;
overflow-x: auto;
color: #4d4d4c;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
/*
* Markdown PDF CSS
*/
body {
font-family: "Meiryo", "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
}
pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
border-radius: 3px;
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: break-word;
}
pre:not(.hljs) {
padding: 23px;
line-height: 19px;
}
blockquote {
background: rgba(127, 127, 127, 0.1);
border-color: rgba(0, 122, 204, 0.5);
}
.emoji {
height: 1.4em;
}
/* for inline code */
:not(pre):not(.hljs) > code {
color: #C9AE75; /* Change the old color so it seems less like an error */
font-size: inherit;
}
/* Page Break : use <div class="page"/> to insert page break
-------------------------------------------------------- */
.page {
page-break-after: always;
}
Problem Statement: Nosco Hiring Challenge
There is an innovation competition across the land, and the various
houses compete. All the ideas have been collected and scored, and now is the time
to calculate the results to find out the most innovative houses.
-
The
innovation scoreof a house is simply theaverage scoresof all the ideas
submitted by people affiliated with this house. -
Higher is better.
-
Ideas with no scores are excluded from consideration.
-
Some people are affiliated with no house, in which case they should be counted as if in the house 'Free folk'.
-
Some people are affiliated with more than one house, in which case the idea is credited to each and every one of the affiliated houses. For example, if user A is affiliated with houses X and Y, and they have submitted an idea that has a score of 5.7, both house X and Y will add a score of 5.7 in their tally.
Input Data
The input data is two JSON files, and their EDN counterparts (containing the same data).
-
users.jsoncontains various users, each having an id first name, last name, email, and potentially a list of their house affiliation(s). -
ideas.jsoncontains various ideas, each one having an id, a title, a body, an author-id (pointing to one entry in theusers.jsonfile) and an array of numeric scores. Unfortunately some scores were lost and were replaced by nulls. These scores should be ignored entirely.
Expected output
The results should include:
an list of the houses, from most innovative to least innovative
the innovation score of each house
the number of ideas submitted by each house
Other considerations
Reasonable performance is expected, but readability of the code is more
important. Use descriptive names and add docstrings and comments as needed.
You're free to use any 3rd-party library that seems suitable for the
task, keeping in mind how it might affecte the readability of the code for someone
who isn't familiar with it.
The code should include instructions on how to run it and get the results.
Any reasonably popular build tool is fine, or a single file with
side-effectful statements will also do. Use the tool that will allow
you to move to actually solving the problem.
There's no need to write a test suite for this exercise.
Solution by Abhinav Sharma (abhi18av@outlook.com)
Design Considerations
-
Could have used
:^privateordefn-if this was a library oriented solution. However, as mentioned in the challenge description I've optimized for readibility rather than less number variables or moreletbindings along with other deep clojure constructs. This would be caliberated as per the team standard. -
Opted to deliver the base solution in the a single
lumo.cljsfile so as to avoid the hassle of setting up a build configs and slow startups. -
I have only used the
clojure standard librarysince, it's already there and I wanted the code to be usable by vanilla installation oflumo. -
Didn't use
specat all. -
Depending on the team's comfort level, standard and design choices, I might have opted for specter or medley to simplify things
Thoughts
-
A shared resource pool would be really helpful like Clojure - The Essential Reference or PurelyFunctional courses. This way, as a team we could be sure of each other's miminal knowledge base.
-
In production for nested queries, I think it might be worth trying out pathom pathom for traversing nested queries.
Solution-1: ClojureScript (via Lumo on Terminal)
Instructions to download and setup lumo
Lumo bundles ClojureScript and NodeJS together as a single binary. More info can be found at https://github.com/anmonteiro/lumo.
I've used it in the past to create the classic snake game using the browser's canvas API - luminus-snake
The core solution is based around a single file ./src/innovation_competition/lumo.cljs which is intended to be used as follows
> cd ./src/innovation_competition/
> lumo lumo.cljs
The solution would be printed out as like so

You could also just compile the lumo.cljs solution to NodeJS ready solution by using the bundled clojurescript compiler like done here luminus-snake/build_snake.cljs
Solution-2: Clojure (via REPLs)
The ./src/innovation_competition/core.clj file is the file which is meant to be used with a REPL as it contains commented code-snippets which could be used to explore the state of variables and the functionality of functions.
-
Terminal REPL
The simplest way to start a repl would be to dolein repland it'll land you in theinnovation_competition.corenamespace. Feel free to play around -
Cursive via IntelliJ
You'll need to open the project in the IntelliJ iwth Curisive plugin installed and the config the local nrepl like so

Once the REPL fires up, you can confirm the namespace you can use *ns*

- Spacemacs
$$ Just open thecore.cljand press,'and you'djack inright into theinnovation_competition.corenamespace

Solution-3: Python (via Jupyter)
This was a really fun problem to work with and I explored the solution using another tool I have, which is Python and so you can find the HTML or the Jupyter Notebook in the ./python directory.

Feedback
-
It was a wonderfully refreshing test and this ability of clojurians to try new things is the reason why I am so fond of clojure and it's community.
-
To avoid the multiple build tools you might need to install to evaluate various other submissions, you could ask people to use nextjournal as it comes with an in-built
clojureandclojurescriptrepl. Think of it as a much better version ofjupyter notebooks.
Things I tried
-
Adding rebel-readline library to the project to make the
lein replcolorful but decided against adding another dependency. -
Exploring the data via cognitect's REBL, couldn't make it work