patternjavascriptMinor
Python DefaultDict implementation in JavaScript
Viewed 0 times
defaultdictimplementationjavascriptpython
Problem
I found Python's
```
(function () {
'use strict';
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
if (thisArg) {
for (i = 0; i < collection.length; ++i) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i = 0; i < collection.length; ++i) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
} else {
if (thisArg) {
for (i in collection) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i in collection) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
}
return collection;
}
function deepCopy(obj) {
if (typeof obj != 'object') {
return obj;
}
var newObj = Array.isArray(obj) ? new Array(obj.length) : {};
forEach(obj, function (v, k) {
newObj[k] = v;
});
return newObj;
}
function assert(expression) {
if (!expression) {
throw 'Assertion faild';
}
}
function implicitEqual(val1, val2) {
return val1 == val2;
}
function explicitEqual(val1, val2) {
return val1 === val2;
}
function deleteFromArray(arr, value, flag) {
var ret = null;
var method = flag == 'i' ? implicitEqual : explicitEqual;
forEach(arr, function (val, index) {
if (method(val, value)) {
arr.splice(index, 1);
ret = val;
return false;
}
})
return
defaultdict and OrderedDict incredibly useful, hence I attempted an implementation in JavaScript. As JavaScript objects only support string as key, it is not as powerful as the Python version. Can anyone suggest a way of using an object as a key in JS, like some kind of hash function? Also, is there anything wrong with this implementation?```
(function () {
'use strict';
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
if (thisArg) {
for (i = 0; i < collection.length; ++i) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i = 0; i < collection.length; ++i) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
} else {
if (thisArg) {
for (i in collection) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i in collection) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
}
return collection;
}
function deepCopy(obj) {
if (typeof obj != 'object') {
return obj;
}
var newObj = Array.isArray(obj) ? new Array(obj.length) : {};
forEach(obj, function (v, k) {
newObj[k] = v;
});
return newObj;
}
function assert(expression) {
if (!expression) {
throw 'Assertion faild';
}
}
function implicitEqual(val1, val2) {
return val1 == val2;
}
function explicitEqual(val1, val2) {
return val1 === val2;
}
function deleteFromArray(arr, value, flag) {
var ret = null;
var method = flag == 'i' ? implicitEqual : explicitEqual;
forEach(arr, function (val, index) {
if (method(val, value)) {
arr.splice(index, 1);
ret = val;
return false;
}
})
return
Solution
Readability
Method
(1) Complex Object
One for looping a complex object:
New method
As an example:
yielding
(2) Array
And one for looping an array:
New method
As an example:
yielding
Don't Repeat Yourself (DRY)
Then we can refactor your method to call either method depending on whether the source is an array and avoid redudancy when
As compared to the initial beast:
The previous examples through your refactored method:
Method
forEach has 4 nested if-statements, with each statement doing things a little differently. This is a good candidate to refactor for compactness. There are 2 distinct ways you are looping arg collection, so I suggest to make a method for each of them.(1) Complex Object
One for looping a complex object:
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
// ..
}
else {
// .. this part
}
}New method
propertyEach:function propertyEach(source, iteratee) {
for (const key in source) {
if (iteratee(source[key], key, source) === false) {
break;
}
}
return source;
}As an example:
const myComplexObject = {
name: 'Doe',
firstName: 'John'
};
propertyEach(myComplexObject, (item, i) => console.log(i + ': ' + item));yielding
'name: Doe'
'firstName: John'(2) Array
And one for looping an array:
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
// .. this part
}
else {
// ..
}
}New method
arrayEach:// courtesy of lodash
function arrayEach(array, iteratee) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}As an example:
const myArray = [ 1, 2, 3 ];
arrayEach(myArray, (item, i) => console.log(i + ': ' + item));yielding
'0: 1'
'1: 2'
'2: 3'Don't Repeat Yourself (DRY)
Then we can refactor your method to call either method depending on whether the source is an array and avoid redudancy when
thisArg is undefined.function forEach(source, func, thisArg) {
if (thisArg === undefined) thisArg = source;
if (Array.isArray(source)) {
arrayEach(source, (item, i) => func.call(thisArg, item, i));
} else {
propertyEach(source, (item, i) => func.call(thisArg, item, i));
}
}As compared to the initial beast:
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
if (thisArg) {
for (i = 0; i < collection.length; ++i) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i = 0; i < collection.length; ++i) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
} else {
if (thisArg) {
for (i in collection) {
if (func.call(thisArg, collection[i], i) === false) {
break;
}
}
} else {
for (i in collection) {
if (func.call(collection[i], collection[i], i) === false) {
break;
}
}
}
}
return collection;
}The previous examples through your refactored method:
forEach(myArray, (item, i) => console.log(i + ': ' + item));
forEach(myComplexObject, (item, i) => console.log(i + ': ' + item));Code Snippets
function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
// ..
}
else {
// .. this part
}
}function propertyEach(source, iteratee) {
for (const key in source) {
if (iteratee(source[key], key, source) === false) {
break;
}
}
return source;
}const myComplexObject = {
name: 'Doe',
firstName: 'John'
};
propertyEach(myComplexObject, (item, i) => console.log(i + ': ' + item));'name: Doe'
'firstName: John'function forEach(collection, func, thisArg) {
var i;
if (Array.isArray(collection)) {
// .. this part
}
else {
// ..
}
}Context
StackExchange Code Review Q#79926, answer score: 3
Revisions (0)
No revisions yet.