I'm stuck with one of curriculum in freeCodeCamp.org
https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/arguments-optional
The below code is what I wrote.
In that code, addTogether(2)(3) should be 5.
But instead of that, addTogether(2)(3) is "undefined"
what's the problem?
I read all hints from freecodecamp forum. But I don't get it.
function addTogether() {
var checkNum = function(x) {
if (typeof x === "number") {
return x
} else {
return undefined
}
}
if (arguments.length > 1) {
if (checkNum(arguments[0]) !== undefined && checkNum(arguments[1]) !== undefined) {
return arguments[0] + arguments[1]
} else {
return undefined
}
} else {
var a = arguments[0]
if (checkNum(a) === undefined) {
return undefined
} else {
return function(args2) {
args2 + a
}
}
}
return false;
}
console.log(addTogether(2)(3))
Your returned function doesn't have a return value. You could use
return function(args2) {
return args2 + a
}
Or
return (args2) => args2 + a
function addTogether(a,b) {
if (arguments.length==2){
if (typeof a == "number" && typeof b == "number"){
return a + b;
}
}
if(arguments.length==1){
if (typeof a == "number"){
return function(b){
if (typeof b == "number"){
return a + b;
}
};
}
}
}
I want to display files that have more then 3 if/for/while/switch/try statements (same as sonar) so far I have code that traverse the tree and count statements for whole file:
var fs = require('fs');
var esprima = require('esprima');
function traverse(obj, fn) {
for (var key in obj) {
if (obj[key] !== null && fn(obj[key]) === false) {
return false;
}
if (typeof obj[key] == 'object' && obj[key] !== null) {
if (traverse(obj[key], fn) === false) {
return false;
}
}
}
}
fs.readFile(process.argv[2], function(err, file) {
var syntax = esprima.parse(file.toString());
var count = 0;
traverse(syntax, function(obj) {
if (obj.type == "TryStatement" || obj.type == "ForStatement" ||
obj.type == "IfStatement" || obj.type == "WhileStatement"
obj.type == "SwitchStatement") {
count++;
}
});
console.log(process.argv[2] + ' ' + count);
});
How can I count path in the program for instance for this code:
try {
if (foo == 10) {
for (var i=0; i<foo; ++i) {
if (i % 5 == 0) {
var j = i;
while(j--) {
console.log(j);
}
}
}
}
} catch(e) {
if (e instanceof Error) {
if (e.stack) {
console.log(e.stack);
}
}
}
it should show 5 and 2 not 7.
I currently have this code built in JS, but it's really, really ugly.
Is there any better way to approach it?
The way it works basically is pushing a string like app.chat.test to be the key, and value like teststr.
I test the lengths to see if the "parent" key is there, otherwise we build it.
function constructJson(jsonKey, jsonValue){
//REWRITE!!!!!!!!
let jsonObj = langFile;
let jsonKeyArr = jsonKey.split('.')
if (jsonKeyArr.length === 1) {
if (valToAdd === undefined) {
if (jsonObj[jsonKey] === undefined) {
jsonObj[jsonKey] = {}
}
} else {
if (jsonObj[jsonKey] === undefined) {
jsonObj[jsonKey] = valToAdd
}
}
} else if (jsonKeyArr.length === 2) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = jsonValue
}
} else if (jsonKeyArr.length === 3) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = jsonValue
}
} else if (jsonKeyArr.length === 4) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] = jsonValue
}
} else if (jsonKeyArr.length === 5) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]][jsonKeyArr[4]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]][jsonKeyArr[4]] = jsonValue
}
} else if (jsonKeyArr.length > 5) {
return console.log("Length over 5 not supported yet!")
}
return jsonObj;
}
Regards.
OF course it's possible, a simple loop will perfeclty do the job.
function constructJson(jsonKey, jsonValue){
//REWRITE!!!!!!!!
langFile = {a:{}, foo:{}};// remove this for your own code
var jsonObj = langFile;
var jsonKeyArr = jsonKey.split('.');
var currentValue = jsonObj;
for(var i = 0; i < jsonKeyArr.length;i++){
if(currentValue[jsonKeyArr[i]]===undefined){
currentValue[jsonKeyArr[i]] = {};
}
if(i < jsonKeyArr.length-1){
currentValue = currentValue[jsonKeyArr[i]];
}else{
currentValue[jsonKeyArr[i]] = jsonValue;
}
}
return jsonObj;
}
alert(JSON.stringify(constructJson("a.b.cd.ef", "toto")));
I just assigning to a temporary variable each sublevel. When i'm on the last i'm assigning the value.
Yes you can, using the javascript reduce function on the array created from the splitted string.
function namespaceCreateExceptLast(representationOfElementToCreate, baseNamespace) {
var tokens;
if (typeof representationOfElementToCreate !== 'string')
throw new Error('Expecting string as first parameter');
if (baseNamespace === undefined)
baseNamespace = window;
tokens = representationOfElementToCreate.split('.');
// Remove the last element (which will contain the value)
tokens.pop();
// Use reduce to create part by part your new object
return tokens.reduce(function (prev, property) {
if (typeof prev !== 'object') {
throw Error('One property is already defined but not an object, namespace creation has failed', property);
return undefined;
} else {
if (!prev[property])
prev[property] = {};
return prev[property];
}
}, baseNamespace);
};
Then you can have:
function constructJson(jsonKey, jsonValue){
let jsonObj = langFile;
var lastItem = namespaceCreateExceptLast(jsonKey, jsonObj);
var lastKey = jsonKey.substring(jsonKey.lastIndexOf('.') + 1);
lastItem[lastKey] = jsonValue;
}
I have added some comments and exceptions to help you understand how it's done, but it's mainly based on the reduce function which you can easily get help for (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce).
Working on a Holdem hand evaluator, and part of the yak shaving is writing a "how many combos of 5 do you get from 7 cards" function (pickNofSet()). I've done that, but the way I've done that returns a bunch of duplicates.
So I have to write a removeDuplicates(). Here's the problem... it works with a simple array, but it doesn't work with the "arrays of arrays" that my "pickNofSet" function generates.
-- here's the removeDuplicates code --
var removeDuplicates = function(input){ // takes array
var output = [];
for (i=0; i < input.length; i++){
var unique = true; // all elements are innocent until proven guilty
for(j=i+1; j < input.length; j++){
if(input[j] === input[i]){
unique = false; // guilty!
};// endif
};// end jfor
if(unique){ // if not found guilty,
output.push(input[i]); // you may go free, little element
};// end if
};// end ifor
console.log(output);
return output; };//end function
Here's what I get from the Console:
> removeDuplicates(['a','b','c'],['a','b','c'],['d','e','f'],['g','h','i']);
< undefined
> removeDuplicates([1, 2, 2, 3, 3, 5, 5, 6, 6, 6]);
< [1, 2, 3, 5, 6]
Operator === can't compare array
As you use === to check equality between two elements, this only works with primitive data types, but not array. e.g.
1===1 // true
[1]===[1] // Sorry, you can't
If you want your algorithm to work with both primitive elements and array elements, you may need to upgrade your equality check from === to a customized function.
From this:
if(input[j] === input[i]){
unique = false; // guilty!
};// endif
To this:
if (equals(input[j],input[i]){
unique = false; // guilty!
};// endif
And implement equals function to be capable of comparing both primitive data types and arrays:
function equals(a,b){
if (typeof(a)!=typeof(b))
return false;
else if (typeof(a)=='object'){
if (Object.keys(a).length != Object.keys(b).length)
return false;
else{
for (var keyA of Object.keys(a)){
if (!(keyA in b))
return false;
else if (a[keyA]!==b[keyA])
return false;
else
return true;
}
}
}
else
return a===b;
}
Hint: This solution should, hopefully, also work with JSON element.
Here is an example of a general purpose unique filter that should fill your need. Requires an ES5 compliant environment.
(function () {
'use strict';
function $strictEqual(a, b) {
return a === b;
}
function $isUndefined(inputArg) {
return $strictEqual(typeof inputArg, 'undefined');
}
function $isPrimitive(inputArg) {
var type = typeof inputArg;
return type === 'undefined' || inputArg === null || type === 'boolean' || type === 'string' || type === 'number' || type === 'symbol';
}
function $isFunction(inputArg) {
return typeof inputArg === 'function';
}
function $isDate(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Date]';
}
function $isRegExp(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object RegExp]';
}
function $isString(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object String]';
}
function $isArguments(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Arguments]';
}
function $getItem(object, index) {
var item;
if ($isString(object)) {
item = object.charAt(index);
} else {
item = object[index];
}
return item;
}
var de = function (a, b, circ) {
if (a === b) {
return true;
}
var aType,
bType,
aIsArgs,
bIsArgs,
aIsPrim,
bIsPrim,
aCirc,
bCirc,
ka,
kb,
length,
index,
it;
if ($isDate(a) && $isDate(b)) {
return a.getTime() === b.getTime();
}
if ($isRegExp(a) && $isRegExp(b)) {
return a.source === b.source &&
a.global === b.global &&
a.multiline === b.multiline &&
a.lastIndex === b.lastIndex &&
a.ignoreCase === b.ignoreCase &&
a.sticky === b.sticky;
}
aIsPrim = $isPrimitive(a);
bIsPrim = $isPrimitive(b);
if ((aIsPrim || $isFunction(a)) && (bIsPrim || $isFunction(b))) {
/*jslint eqeq: true */
return a == b;
}
if (aIsPrim || bIsPrim) {
return a === b;
}
if (a.prototype !== b.prototype) {
return false;
}
if (circ.a.indexOf(a) === -1) {
circ.a.push(a);
} else {
aCirc = true;
}
if (circ.b.indexOf(b) === -1) {
circ.b.push(b);
} else {
bCirc = true;
}
if (aCirc && bCirc) {
circ.cnt += 1;
} else {
circ.cnt = 0;
}
if (circ.cnt > 200) {
throw new RangeError('Circular reference limit exceeded');
}
aIsArgs = $isArguments(a);
bIsArgs = $isArguments(b);
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) {
return false;
}
if (aIsArgs) {
return de(Array.prototype.slice.call(a), Array.prototype.slice.call(b), circ);
}
ka = Object.keys(a);
kb = Object.keys(b);
length = ka.length;
if (length !== kb.length) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
} else {
return false;
}
} else {
ka.sort();
kb.sort();
for (index = 0; index < length; index += 1) {
if (ka[index] !== kb[index]) {
return false;
}
}
}
for (index = 0; index < length; index += 1) {
it = ka[index];
if (!de($getItem(a, it), $getItem(b, it), circ)) {
return false;
}
}
aType = typeof a;
bType = typeof b;
return aType === bType;
};
if (!Object.prototype.deepEqual) {
Object.defineProperty(Object.prototype, 'deepEqual', {
enumerable: false,
configurable: true,
writable: true,
value: function (b) {
var a = this;
return de(a, b, {
a: [],
b: [],
cnt: 0
});
}
});
}
if (!Array.prototype.unique) {
Object.defineProperty(Array.prototype, 'unique', {
enumerable: false,
configurable: true,
writable: true,
value: function (equalFn, thisArg) {
var object = Object(this),
length,
index,
eqFn,
arr,
idx,
val,
it;
if ($isUndefined(equalFn)) {
eqFn = $strictEqual;
} else {
eqFn = equalFn;
}
if (!$isFunction(eqFn)) {
throw new TypeError('Argument is not a function: ' + eqFn);
}
arr = [];
length = object.length >>> 0;
for (index = 0; index < length; index += 1) {
if (index in object) {
it = $getItem(object, index);
val = true;
for (idx = 0; idx < length; idx += 1) {
if (idx < index && idx in object && eqFn.call(thisArg, it, $getItem(object, idx))) {
val = false;
break;
}
}
if (val) {
arr.push(it);
}
}
}
return arr;
}
});
}
}());
var data1 = [1, 2, 2, 3, 3, 5, 5, 6, 6, 6],
data2 = [
['a', 'b', 'c'],
['a', 'b', 'c'],
['d', 'e', 'f'],
['g', 'h', 'i']
],
equals = Function.prototype.call.bind(Object.prototype.deepEqual),
pre = document.getElementById('out');
pre.textContent = JSON.stringify(data1.unique(equals), null, 2);
pre.textContent += '\n\n';
pre.textContent += JSON.stringify(data2.unique(equals), null, 2);
<pre id="out"></pre>
So I have created a constructor that I am attempting to prototype. I want a method that checks through each property in an object to see if its empty and if it is it returns the key. If the property is an object I want it to check through that sub object as well.
UPDATED:
My code thus far:
function Properties(val1, val2, val3, val4){
this.prop1 = val1 || "";
this.prop2 = val2 || "";
this.prop3 = val3 || "";
this.prop4 = val4 || {};
}
Properties.prototype = {
isEmpty: function(){
for (key in this) {
if(typeof this[key] == "object" && this[key] !== null){
this[key].isEmpty();
} else {
if(!this[key]){
console.log(key);
}
}
}
}
}
var test = new Properties("Something", "", "", {subProp1: "Something Else", subProp2: "", subProp3: {subSubProp1: "", subSubProp2: "" }});
The method should return prop2, prop3, subProp2, subSubProp1, subSubProp2
That method isn't a property on the object. You need to pass in the object in question. You can also pass an array in to keep track of the empty keys:
var emptyKeys = [];
function isEmpty(obj, keysArr) {
for (var key in obj) {
if (typeof obj[key] === "object" && obj.hasOwnProperty(key)) {
isEmpty(obj[key], keysArr);
} else {
if (obj[key] == "" || obj[key] == null) {
keysArr.push(key);
}
}
}
}
Demo: http://jsfiddle.net/17rt0qy3/1/
If you want this on the actual object, simply add the above function inside the isEmpty function:
isEmpty: function(){
var emptyKeys = [];
amIEmpty(this, emptyKeys);
return emptyKeys;
//Actual logic
function amIEmpty(obj, keysArr) {
for (var key in obj) {
if (key == "isEmpty") {
continue;
}
if (typeof obj[key] === "object" && obj.hasOwnProperty(key)) {
amIEmpty(obj[key], keysArr);
} else {
if (obj[key] == "" || obj[key] == null) {
keysArr.push(key);
}
}
}
}
}
Demo: http://jsfiddle.net/17rt0qy3/2/
And a fiddle working with your demo object above: http://jsfiddle.net/17rt0qy3/3/
Aaand another edit, this will only log the keys, but it's a bit cleaner:
isEmpty: function(obj, keys) {
keys = keys || [];
obj = obj || this;
for (var key in obj) {
if (typeof obj[key] === "object" && obj.hasOwnProperty(key)) {
this.isEmpty(obj[key], keys)
} else {
if (obj[key] == "" || obj[key] == null) {
keys.push(key);
}
}
}
return keys;
}
Demo: http://jsfiddle.net/17rt0qy3/8/