Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[nodejs] MongoDB and Mongoose Challenges

These are my notes while doing the course APIs and Microservices on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


MongoDB is a database that stores data records (documents) for use by an application. Mongo is a non-relational, “NoSQL” database. This means Mongo stores all associated data within one record, instead of storing it across many preset tables as in a SQL database.
Mongo’s use of JSON as its document storage structure makes it a logical choice when learning backend JavaScript. Accessing documents and their properties is like accessing objects in JavaScript.

Mongoose.js is an npm module for Node.js that allows you to write objects for Mongo as you would in JavaScript.

MongoDB Atlas is a MongoDB Database-as-a-Service platform.


Install and Set Up Mongoose

Add mongodb and mongoose to the project’s package.json.

    "dependencies": {
        "body-parser": "^1.15.2",
        "dotenv": "^8.2.0",
        "express": "^4.12.4",
        "mongodb": "^3.6.4",
        "mongoose": "^5.11.15"
    },

Store your MongoDB Atlas database URI in a private .env file as MONGO_URI. Replace user and password.

MONGO_URI=mongodb+srv://<User>:<Password>@cluster0.xvsqx.mongodb.net/<dbname>?retryWrites=true&w=majority

Connect to the database using the following syntax:

const mongoose = require('mongoose');
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });

Create a Model

Everything in Mongoose starts with a Schema. Each schema maps to a MongoDB collection and defines the shape of the documents within that collection.

const { Schema } = mongoose;

const personSchema = new Schema({
  name: { type: String, required: true },
  age: Number,
  favoriteFoods: [String]
});

const Person = mongoose.model("Person", personSchema);

Create and Save a Record of a Model

The done() function is a callback that tells us that we can proceed after completing an asynchronous operation such as inserting, searching, updating, or deleting. It’s following the Node convention, and should be called as done(null, data) on success, or done(err) on error.

const createAndSavePerson = (done) => {

  let max = new Person({name: "Max", age: 31, favoriteFoods: ["Pasta"]});

  max.save((err, data) => {
    if (err) return console.error(err);
    done(null, data)
  });

};

Create Many Records with model.create()

var arrayOfPeople = [
    {name: "Max", age: 31, favoriteFoods: ["Pasta"]},
    {name: "Toni", age: 21, favoriteFoods: ["Pizza"]},
    {name: "Paul", age: 34, favoriteFoods: ["Bolo", "Penne"]}
    ];

const createManyPeople = (arrayOfPeople, done) => {
  Person.create(arrayOfPeople, (err, people) => {
    if (err) return console.error(err);
    done(null, people);
  });
};

Use model.find() to Search Your Database

Model.find() accepts a query document (a JSON object) as the first argument, then a callback. It returns an array of matches.

const findPeopleByName = (personName, done) => {
  Person.find({name: personName}, (err, personFound) => {
    if (err) return console.error(err);
    done(null, personFound);
  })
};

Use model.findOne() to Return a Single Matching Document from Your Database

Model.findOne() behaves like Model.find(), but it returns only one document (not an array), even if there are multiple items.

const findOneByFood = (food, done) => {
  Person.findOne({favoriteFoods: food}, (err, personFound) => {
    if (err) return console.error(err);
    done(null, personFound);
  })
};

Use model.findById() to Search Your Database By _id

When saving a document, MongoDB automatically adds the field _id, and set it to a unique alphanumeric key.

const findPersonById = (personId, done) => {
  Person.findById({_id: personId}, (err, personFound) => {
    if (err) return console.error(err);
    done(null, personFound);
  })
};

Perform Classic Updates by Running Find, Edit, then Save

const findEditThenSave = (personId, done) => {
  const foodToAdd = 'hamburger';

  Person.findById(personId, (err, person) => {
    if(err) return console.log(err); 
  
    person.favoriteFoods.push(foodToAdd);
    person.save((err, updatedPerson) => {
      if(err) return console.log(err);
      done(null, updatedPerson)
    })
  })
};

Perform New Updates on a Document Using model.findOneAndUpdate()

Use the function parameter personName as the search key. Set the person’s age to 20.
Note: You should return the updated document. To do that, you need to pass the options document { new: true } as the 3rd argument to findOneAndUpdate(). By default, these methods return the unmodified object.

const findAndUpdate = (personName, done) => {
  const ageToSet = 20;

  Person.findOneAndUpdate({name: personName}, {age: ageToSet}, { new: true }, (err, updatedDoc) => {
    if (err) return console.error(err);
    done(null, updatedDoc)
  })
};

Delete One Document Using model.findByIdAndRemove()

const removeById = (personId, done) => {

  Person.findByIdAndRemove(personId, (err, personDeleted) => {
    if (err) return console.error(err);
    done(null, personDeleted)
  })
};

Delete Many Documents with model.remove()

const removeManyPeople = (done) => {
  const nameToRemove = "Mary";

  Person.remove({name: nameToRemove}, (err, personsDeleted) => {
    if (err) return console.error(err);
    done(null, personsDeleted)
  })
};

Chain Search Query Helpers to Narrow Search Results

const queryChain = (done) => {
  const foodToSearch = "burrito";

  Person.find({favoriteFoods: foodToSearch})
  .sort({name: 1}) //sort bei name
  .limit(2) //only 2 results
  .select({age: 0})  //hide age
  .exec((err, twoPersonFound) => {
    if (err) return console.error(err);
    done(null, twoPersonFound)
  })
};

[nodejs] Basic Node and Express

These are my notes while doing the course APIs and Microservices on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


Node.js is a JavaScript runtime that allows developers to write backend (server-side) programs in JavaScript. Node.js comes with a handful of built-in modules (small, independent programs) that help facilitate this purpose. Some of the core modules include:

  • HTTP: a module that acts as a server
  • File System: a module that reads and modifies files
  • Path: a module for working with directory and file paths
  • Assertion Testing: a module that checks code against prescribed constraints

Express (not included by default) runs between the server created by Node.js and the frontend pages of a web application. Also handles the app’s routing.

https://github.com/freeCodeCamp/boilerplate-express/


Start a Working Express Server

Let’s serve our first string! In Express, routes takes the following structure: app.METHOD(PATH, HANDLER). METHOD is an http method in lowercase. PATH is a relative path on the server. HANDLER is a function that Express calls when the route is matched.

app.get("/", (req, res) => {
  res.send("Hello Express");
});

Serve an HTML File

You can respond to requests with a file using the res.sendFile(path) method. This method needs an absolute file path. Use the Node global variable __dirname to calculate the path.
Send the /views/index.html file as a response to GET requests to the / path.

let absolutePath = __dirname + "/views/index.html";

app.get("/", (req, res) => {
  res.sendFile(absolutePath);
});

Serve Static Assets

Using express middleware to serve static files (stylesheets, scripts, images): app.use(path, middlewareFunction)

let staticFilesPath = __dirname + "/public";

app.use("/", express.static(staticFilesPath));

Serve JSON on a Specific Route

A REST (REpresentational State Transfer) API allows data exchange in a simple way, without the need for clients to know any detail about the server.

let dataJson = {"message": "Hello json"};

app.get("/json", (req, res) => {
  res.json(dataJson);
});

Use the .env File

The .env file is a hidden shell file that is used to pass environment variables to your application. Accessible from the app as process.env.VAR_NAME. Add variables to .env with this syntax: VAR_NAME=value

let dataJson = {"message": "Hello json"};

app.get("/json", (req, res) => {
 if (process.env.MESSAGE_STYLE === "uppercase") {
   dataJson.message = dataJson.message.toUpperCase();
 };

 res.json(dataJson);
});

Implement a Root-Level Request Logger Middleware

For every request, it should log to the console a string taking the following format: method path - ip. An example would look like this: GET /json - ::ffff:127.0.0.1.

app.use(function middleware(req, res, next) {
  console.log(req.method + " " + req.path + " - " + req.ip);
  next();
});

Chain Middleware to Create a Time Server

app.get('/now', 
(req, res, next) => {
  req.time = new Date().toString();
  next();
},  
(req, res) => {
  res.json({time: req.time})
});

Get Route Parameter Input from the Client

route_path: ‘/:word/echo’
actual_request_URL: ‘/myString/echo’
req.params: {word: ‘myString’}

app.get("/:word/echo", (req, res) => {
  res.json({"echo": req.params.word});
});

Get Query Parameter Input from the Client

route_path: ‘/name’
actual_request_URL: ‘/name?first=firstname&last=lastname’
req.query: {first: ‘firstname’, last: ‘lastname’}

app.get("/name", (req, res) => {
  res.json({name: `${req.query.first} ${req.query.last}`})
});

Use body-parser to Parse POST Requests

POST is the default method used to send client data with HTML forms. In REST convention, POST is used to send data to create new items in the database (a new user, or a new blog post).
In these kind of requests, the data doesn’t appear in the URL, it is hidden in the request body. The body is a part of the HTTP request, also called the payload.
Add "body-parser": "^1.15.2", in your package.json as dependencie to parse the body data.

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

Get Data from POST Requests

Mount a POST handler at the path /name.

app.post("/name", function(req, res) {
  res.json({name: `${req.body.first} ${req.body.last}`})
});

[nodejs] Managing Packages with NPM

These are my notes while doing the course APIs and Microservices on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


The Node Package Manager (npm) is a command-line tool to share and control modules (or packages) of JavaScript code written for use with Node.js.

When starting a new project, npm generates a package.json file. This file lists the package dependencies for your project. Since npm packages are regularly updated, the package.json file allows you to set specific version numbers for each dependency. This ensures that updates to a package don’t break your project.

npm saves packages in a folder named node_modules. These packages can be installed in two ways:

  1. globally in a root node_modules folder, accessible by all projects.
  2. locally within a project’s own node_modules folder, accessible only to that project.

The package.json file is the center of any Node.js project or npm package. It stores information about your project, similar to how the <head> section of an HTML document describes the content of a webpage. It consists of a single JSON object where information is stored in key-value pairs.

https://github.com/freeCodeCamp/boilerplate-npm/

{
  "name": "fcc-learn-npm-package-json", // your project name
  "author": "Max Mustermann",
  "description": "A project that does something awesome",
  "keywords": [ "descriptive", "related", "words", "freecodecamp" ],
  "license": "MIT", // inform users of what they are allowed to do with your project
  "version": "1.2.0", // describes the current version of your project
  "dependencies": {
    "package-name": "MAJOR.MINOR.PATCH", // Semantic Versioning
    "express": "^4.14.0", 
    "moment": "~2.10.2" // handy library for working with time and dates.
  }
}

PATCHes are bug fixes and MINORs add new features but neither of them break what worked before. Finally, MAJORs add changes that won’t work with earlier versions. Find a more detailed explanation here.

To allow an npm dependency to update to the latest PATCH version, you can prefix the dependency’s version with the tilde (~) character.
The caret (^) will allow both MINOR updates and PATCHes.

[macOS] Boop

“Boop is a simple editor that allows you to execute scripts on the buffer. The idea is that you don’t have to paste potentially secret information into shady websites to do some simple transforms, like format json and decoding query strings.”

https://boop.okat.best/

There is also a port for Linux and Windows: https://github.com/zoeyfyi/Boop-GTK

sudo install boop-gtk.linux.amd64 /usr/local/bin/boop-gtk

Run from terminal with boop-gtk

[Linux Mint] Install PyWal on Linux Mint 20.1 Cinnamon

“Pywal is a tool that generates a color palette from the dominant colors in an image. It then applies the colors system-wide and on-the-fly in all of your favorite programs.”

https://github.com/dylanaraps/pywal/wiki/Installation

pip3 install pywal

I’m using Variety to change my wallpaper every day automatically. To always get the right colors in my terminal I added some lines in my .zshrc that will always grab the current wallpaper and pass it to PyWal. I’m sure a bash pro would do this in just one line… 🙂

#---PyWal---#
# load previous theme
(cat ~/.cache/wal/sequences &)
# get picture path
picturepath=$(gsettings get org.gnome.desktop.background picture-uri) 
# remove prefix & suffix
prefix="'file://" 
suffix="'"
picturepath=${picturepath#"$prefix"} 
picturepath=${picturepath%"$suffix"}
# set colors
wal -n -q -i "$picturepath"
#---PyWal End---#

There are many plugins/tools you can combine with PyWal:

https://github.com/frewacom/pywalfox

https://github.com/khanhas/Spicetify

https://github.com/themix-project/oomox

[JavaScript] Algorithms and Data Structures Projects

These are my notes while doing the course JavaScript Algorithms and Data Structures on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


1. Palindrome Checker

function palindrome(str) {

  // clear string
  str = str.toLowerCase().replace(/[^a-z0-9]/g, "");

  // reverse string
  let strBack = str.split("").reverse().join("");

  // palindrome?
  return (str === strBack) ? true : false;

}

palindrome("_eye");
palindrome("five|\_/|four");
palindrome("1 eye for of 1 eye.");

2. Roman Numeral Converter

function convertToRoman(num) {

  const lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1};
  let roman = '';

  for ( let i in lookup ) {
    while ( num >= lookup[i] ) {
      roman += i;
      num -= lookup[i];
    }
  }
  return roman;
}

convertToRoman(6);

3. Caesars Cipher

function rot13(str) {

  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  const length = alphabet.length;
  let decode = "";

  const getROT13 = index => {
    return (index+13 >= length) ? alphabet[13-(length-index)] : alphabet[index+13];
  }

  for (let i=0; i<str.length; i++) {

    let index = alphabet.indexOf(str[i]);

    decode += (index<0) ? str[i] : getROT13(index); 

  }

  return decode;
}

rot13("SERR PBQR PNZC")
rot13("SERR CVMMN!")

4. Telephone Number Validator

function telephoneCheck(str) {

  let regexCheck = /^(1.*){0,1}(\d{3}|[(]\d{3}[)])[\s|-]*\d{3}.*\d{4}$/g; 

  let result = regexCheck.test(str);

  if (result) {
    let length = str.match(/\d/g).length;
    result = (length == 7 || length == 10 || (length == 11 && str[0] == 1)) ? true : false;
  }

  console.log(str.match(regexCheck));
  console.log(result);

  return result;

}


telephoneCheck("555-555-5555");
telephoneCheck("1 555-555-5555");
telephoneCheck("1 (555) 555-5555");
telephoneCheck("5555555555");
telephoneCheck("555-555-5555")
telephoneCheck("(555)555-5555")
telephoneCheck("1(555)555-5555")
telephoneCheck("1 555)555-5555") //false
telephoneCheck("(6054756961)")  //false
telephoneCheck("2 (757) 622-7382") //false
telephoneCheck("10 (757) 622-7382") //false

5. Cash Register

function checkCashRegister(price, cash, cid) {

  let changeArr = [];

  const currency = { "ONE HUNDRED": 100, "TWENTY": 20, "TEN": 10, "FIVE": 5, "ONE": 1, "QUARTER": 0.25, "DIME": 0.1, "NICKEL": 0.05, "PENNY": 0.01 }

  const substract = (a, b) => parseFloat(a - b).toPrecision(4);

  const calcAmount = (change, curr) => {
                let rest = change % curr;
                let div = substract(change, rest) / curr;    
                return div * curr;
  }

  let change = substract(cash, price);

  //check if cid equals to change
  //or if cid is already not enough
  let sum = 0;
  for (let k=0; k<cid.length;k++) {
    sum += cid[k][1]
  }
  if (sum == change) {
    return {status: "CLOSED", change: cid};
  } else if (sum < change) {
    return {status: "INSUFFICIENT_FUNDS", change: []};
  }

  //calculate change
  for (let i in currency) {
    if (change > currency[i]) {    
      for (let j=0; j<cid.length;j++) {
        if (i == cid[j][0]) {
          let amount = cid[j][1];
          if (substract(change, amount) < 0)  {
            amount = calcAmount(change, currency[i]);
          }
          change = substract(change, amount);
          changeArr.push([i, amount]);
        }
      }
    } 
  }

  if (change != 0) {
    //not able to give the correct change
    return {status: "INSUFFICIENT_FUNDS", change: []};
  }
  return {status: "OPEN", change: changeArr};
}

checkCashRegister(19.5, 20, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]);

checkCashRegister(3.26, 100, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]])

checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])

checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 1], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])

checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])

[JavaScript] Intermediate Algorithm Scripting

These are my notes while doing the course JavaScript Algorithms and Data Structures on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


1. Sum All Numbers in a Range

function sumAll(arr) {

  let num1 = Math.min(arr[0], arr[1]);
  let num2 = Math.max(arr[0], arr[1]);
  let result = 0;

  while (num1 < num2) {
      result += num1;
      num1++;
  }
  return result += num2;
}

sumAll([1, 4]);

2. Diff Two Arrays

function diffArray(arr1, arr2) {

  var newArr = [];

  for (let i = 0; i < arr1.length; i++) {
    if (arr2.indexOf(arr1[i]) == -1) {
      newArr.push(arr1[i]);
    }
  }

  for (let i = 0; i < arr2.length; i++) {
    if (arr1.indexOf(arr2[i]) == -1) {
      newArr.push(arr2[i]);
    }
  }

  return newArr;
}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

3. Seek and Destroy

function destroyer(...arr) {

  let newArr = arr.shift();

  for (let i = 0; i < arr.length; i++) {
    while (newArr.indexOf(arr[i]) > -1) {
      newArr.splice(newArr.indexOf(arr[i]), 1);
    };
  }

  return newArr;
}

destroyer([1, 2, 3, 1, 2, 3], 2, 3);

4. Wherefore art thou

function whatIsInAName(collection, source) {

  var arr = [];

  var srcKeys = Object.keys(source);

  for (let i = 0; i < collection.length; i++) {
    let obj = collection[i];
    let bool = true;

    for (let j = 0; j < srcKeys.length; j++) {
      let key = srcKeys[j];
      if (!obj.hasOwnProperty(key) || (obj[key] != source[key])) {
        bool = false;
      }
    }

    if (bool) {
      arr.push(obj);
    }
  }

  return arr;
}

whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", //last: "Capulet" }], { last: "Capulet" });

whatIsInAName([{ "apple": 1, "bat": 2 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 });

5. Spinal Tap Case

function spinalCase(str) {

  let myRegex = /[A-Z][a-z]+|[a-z]+/g;
  let arr = str.match(myRegex);

  return arr.join("-").toLowerCase();
}

spinalCase('This Is Spinal Tap');
spinalCase("thisIsSpinalTap");
spinalCase("The_Andy_Griffith_Show");
spinalCase("Teletubbies say Eh-oh");
spinalCase("AllThe-small Things");

6. Pig Latin

function translatePigLatin(str) {

  let regexAY = /(^[^aeiou]+)/g;
  let regexWAY = /(^[aeiou]+)/g; 
  
  if (regexAY.test(str)) {
    let cutStr = str.match(regexAY);
    let newStr = str.substr(cutStr[0].length);
    return newStr + cutStr[0] + "ay";

  } else if (regexWAY.test(str)) {
    return str + "way";
  }

}

translatePigLatin("consonant");
translatePigLatin("paragraphs");
translatePigLatin("glove");
translatePigLatin("algorithm");
translatePigLatin("eight");
translatePigLatin("rhythm");

7. Search and Replace

function myReplace(str, before, after) {

  if (/^[A-Z]/.test(before)) {
    after = after.charAt(0).toUpperCase() + after.slice(1);
  } else {
    after = after.charAt(0).toLowerCase() + after.slice(1);
  }

  return str.replace(before, after);

}

myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped");
myReplace("Let us go to the store", "store", "mall");
myReplace("He is Sleeping on the couch", "Sleeping", "sitting");

8. DNA Pairing

function pairElement(str) {

  let arr = [];

  for (let i = 0; i < str.length; i++) {

    let tmp = [];
    tmp.push(str[i]);
    
    switch(str[i]) {
      case "A":
        tmp.push("T");
        break;
      case "T":
        tmp.push("A");
        break;
      case "C":
        tmp.push("G");
        break;
      case "G":
        tmp.push("C");
    }
  
    arr.push(tmp);
  }
  
  return arr;
}

pairElement("GCG");

9. Missing letters

function fearNotLetter(str) {

  const alphabet = "abcdefghijklmnopqrstuvwxyz";

  let startIndex = alphabet.indexOf(str[0]);

  const compareStr = alphabet.slice(startIndex, startIndex + str.length);

  for (let i = 0; i < compareStr.length; i++) {
    if (compareStr[i] !== str[i]) {
        return compareStr[i];
    }
  }

  return undefined;
}

fearNotLetter("abce");
fearNotLetter("bcdf");
fearNotLetter("stvwx");

10. Sorted Union

function uniteUnique(...arr) {

  const uniteArray = [].concat(...arr);

  let uniteUniqueArray = [];

  for (let i = 0; i < uniteArray.length; i++) {
    if (uniteUniqueArray.indexOf(uniteArray[i]) == -1) uniteUniqueArray.push(uniteArray[i]);
  }

  return uniteUniqueArray;
}

uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);

11. Convert HTML Entities

function convertHTML(str) {

  return str
  .replace(/&/g, "&")
  .replace(/</g, "<")
  .replace(/>/g, ">")
  .replace(/"/g, """)
  .replace(/'/g, "'")
  .replace(/<>/g, "<>");

}

convertHTML("Dolce & Gabbana");
convertHTML("Hamburgers < Pizza < Tacos");
convertHTML("Sixty > twelve");
convertHTML('Stuff in "quotation marks"');
convertHTML("Schindler's List");
convertHTML("<>");
convertHTML("abc");

12. Sum All Odd Fibonacci Numbers

function sumFibs(num) {

  let fibo = [1, 1];

  const nextNum = fibu => fibo[fibo.length - 1] + fibo[fibo.length - 2];

  while (nextNum(fibo) <= num) {
    fibo.push(nextNum(fibo));
  };

  return fibo
  .filter(currentNum => currentNum%2 != 0) //odd
  .reduce((sum, currentNum) => sum + currentNum, 0); 

}

sumFibs(1);
sumFibs(4);
sumFibs(1000)
sumFibs(4000000);

13. Sum All Primes

function sumPrimes(num) {

  let primArr = [];

  const isPrime = num => { for(let i = 2; i < num; i++)
                            if(num % i === 0) return false;
                           return num > 1;
                          }

  for (let i = 0; i <= num; i++) {
    if(isPrime(i)) primArr.push(i);
  }

  return primArr.reduce((sum, currentNum) => sum + currentNum, 0);
}

sumPrimes(10);
sumPrimes(977);

14. Smallest Common Multiple

function smallestCommons(arr) {

  let rangeArr = [...arr];

  for (let i = Math.min(...arr) + 1; i < Math.max(...arr); i++) {
    rangeArr.push(i);
  }

  rangeArr.sort((a, b) => b-a);

  let lar = rangeArr[0];
  let scm = lar;
  let bool = false;

  while(!bool){
    
    bool = true;

    for (let i = 0; i < rangeArr.length; i++) {
      if (scm % rangeArr[i] != 0) bool = false;
    }
    
    if (bool) return scm;
    
    scm += lar;
  }

}


smallestCommons([1,5]);

15. Drop it

function dropElements(arr, func) {

  let copy = [...arr];

  for (let i = 0; i < arr.length; i++) {

    if (func(arr[i])) {
      break;
    }

    copy.shift();
    
  }

  return copy;
}

dropElements([1, 2, 3], function(n) {return n < 3; });

16. Steamroller (Recursion)

function steamrollArray(arr) {
 
  var flatArr = [];

  const flatten = arr => {
    for (let i = 0; i < arr.length; i++){
      (Array.isArray(arr[i])) ? flatten(arr[i]) : flatArr.push(arr[i]);
    }
    return flatArr;
  }
  
  return flatten(arr);
}

console.log(steamrollArray([1, [2], [3, [[4]]]]));

17. Binary Agents

function binaryAgent(binary) {

  return binary.split(' ') //Split string in array of binary chars
   .map(bin => String.fromCharCode(parseInt(bin, 2))) //Map every binary char to real char
   .join(''); //Join the array back to a string

}

binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111");

18. Everything Be True

function truthCheck(collection, pre) {
  
  return collection.every(col => col.hasOwnProperty(pre) && Boolean(col[pre]));

}

truthCheck([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex");

19. Arguments Optionalf (was really tough… this is the sample solution)

function addTogether(first, second) {
  if (typeof first !== "number") {
    return undefined;
  }
  const sum = second =>
    typeof second === "number" ? first + second : undefined;
  return typeof second === "undefined" ? second => sum(second) : sum(second);
}

addTogether(2,3);
addTogether("http://bit.ly/IqT6zt");
addTogether(2, "3");
console.log(addTogether(2)([3]));

20. Make a Person

var Person = function(firstAndLast) {

  var _arr = firstAndLast.split(" ");

  // getter
  this.getFirstName = () => _arr[0];
  this.getLastName = () => _arr[1];
  this.getFullName = () => _arr.join(" ");

  // setter
  this.setFirstName = first => _arr[0] = first;
  this.setLastName = last => _arr[1] = last;
  this.setFullName = firstAndLast => _arr = firstAndLast.split(" ");

};

var bob = new Person('Bob Ross');
bob.getFullName();

21. Map the Debris

function orbitalPeriod(arr) {
  var GM = 398600.4418;
  var earthRadius = 6367.4447;
  
  for(let i in arr){
    arr[i].orbitalPeriod = Math.round((2*Math.PI)*Math.sqrt(Math.pow((earthRadius+arr[i].avgAlt),3)/GM));
    delete arr[i].avgAlt;
  }
  return arr;
}

orbitalPeriod([{name : "sputnik", avgAlt : 35873.5553}]);
orbitalPeriod([{name: "iss", avgAlt: 413.6}, {name: "hubble", avgAlt: 556.7}, {name: "moon", avgAlt: 378632.553}]) 

[JavaScript] Functional Programming

These are my notes while doing the course JavaScript Algorithms and Data Structures on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


Functional programming follows a few core principles:

  • Functions are independent from the state of the program or global variables. They only depend on the arguments passed into them to make a calculation
  • Functions try to limit any changes to the state of the program and avoid changes to the global objects holding data
  • Functions have minimal side effects in the program

Callbacks are the functions that are slipped or passed into another function to decide the invocation of that function. You may have seen them passed to other methods, for example in filter, the callback function tells JavaScript the criteria for how to filter an array.

Functions that can be assigned to a variable, passed into another function, or returned from another function just like any other normal value, are called first class functions. In JavaScript, all functions are first class functions.

The functions that take a function as an argument, or return a function as a return value are called higher order functions.

When the functions are passed in to another function or returned from another function, then those functions which gets passed in or returned can be called a lambda.


Pass Arguments to Avoid External Dependence in a Function

Example: Adding/Removing a book to/from a bookList without changing the global bookList array. Instead it returns a new array.

var bookList = ["The Hound of the Baskervilles", "On The Electrodynamics of Moving Bodies", "Philosophiæ Naturalis Principia Mathematica", "Disquisitiones Arithmeticae"];

/* This function should add a book to the list and return the list */
function add(list, bookName) {
  return [...list, bookName];
}

/* This function should remove a book from the list and return the list */
function remove(list, bookName) {
  return list.filter(book => book !== bookName);
}

var newBookList = add(bookList, 'A Brief History of Time');
var newerBookList = remove(bookList, 'On The Electrodynamics of Moving Bodies');
console.log(bookList); // still contains the same books

The remove function could also look like this:

function remove(arr, bookName) {
  let newArr = [...arr]; 
  if (newArr.indexOf(bookName) >= 0) {
    newArr.splice(newArr.indexOf(bookName), 1);
    return newArr; 
  }
}

The methods map(), filter(), slice(), concat() and reduce() are pure functions, as their output depends solely on its inputs and does not mutate the original array.

map() iterates over each item in an array and returns a new array containing the results of calling the callback function on each element. It does this without mutating the original array.
When the callback is used, it is passed three arguments. The first argument is the current element being processed. The second is the index of that element and the third is the array upon which the map method was called.

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'camperCat', age: 10 }
];

const names = users.map(user => user.name); // [ 'John', 'Amy', 'camperCat' ]

A map() implementation could look like this:

var s = [23, 65, 98, 5]; // global Array

Array.prototype.myMap = function(callback) {
  var newArray = [];
  for (let i = 0; i < this.length; i++) {
    newArray.push(callback(this[i]));
  }
  return newArray;
};

var new_s = s.myMap(function(item) {
  return item * 2;
});

filter() calls a function on each element of an array and returns a new array containing only the elements for which that function returns true.
The callback function accepts three arguments. The first argument is the current element being processed. The second is the index of that element and the third is the array upon which the filter method was called.

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'camperCat', age: 10 }
];

const usersUnder30 = users.filter(user => user.age < 30); // [ { name: 'Amy', age: 20 }, { name: 'camperCat', age: 10 } ]

A filter() implementation could look like this:

var s = [23, 65, 98, 5]; // global Array

Array.prototype.myFilter = function(callback) {
  var newArray = [];
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i])) {
        newArray.push(this[i]);
      }
  }
  return newArray;
};

var new_s = s.myFilter(function(item) {
  return item % 2 === 1;
});

slice() returns a copy of certain elements of an array.

var arr = ["Cat", "Dog", "Tiger", "Zebra"];
var newArray = arr.slice(1, 3); // ["Dog", "Tiger"]

concat() combines arrays into a new one without mutating the original arrays.

[1, 2, 3].concat([4, 5, 6]); // Returns a new array [1, 2, 3, 4, 5, 6]

reduce() iterates over each item in an array and returns a single value (i.e. string, number, object, array) via a callback function that is called on each iteration.

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'Max', age: 10 }
];

const usersObj = users.reduce((obj, user) => {
  obj[user.name] = user.age;
  return obj;
}, {});
console.log(usersObj); // { John: 34, Amy: 20, Max: 10 }