I need a solution for the following problem.
I have an array, but there are various types data in it.
Example: [1,2,'string1','string2']
I need a function that does intersection, but for only the matching types.
This is what I mean:
intersect([1,2,'string1','string2'],['string2','string3']) // [1,2,'string2']
intersect([1,2,'string1','string2'],[2,3]) // [2,'string1','string2']
The second argument is always the same type, it is not mixed.
I have this:
function intersect(a, b) {
var t;
if (b.length > a.length) t = b, b = a, a = t; // indexOf to loop over shorter
return a.filter(function (e) {
return b.indexOf(e) > -1;
});
}
but it always removes the other type too :(
function intersect(a, b) {
var t;
if (b.length > a.length) t = b, b = a, a = t; // indexOf to loop over shorter
const type = typeof(b[0]); //check the type of the second array's first element since each element has the same type
const tempArray = a.filter(function (e) {
return typeof(e) !== type;
}); //filter the first array if it has different item types than the second array's elements
return tempArray.concat(a.filter(function (e) {
return b.indexOf(e) > -1;
}));
}
console.log(intersect([1,2,'string1','string2'],['string2','string3'])) // [1,2,'string2']
I wonder how i can convert this curry function to work with n number of variables instead of 2.
so for example i want to invoke:
curry(mul)(3)(3)(3) -> and get 27
(with a fixed version of curry)
function curry(fn) {
return function() {
if (fn.length > arguments.length) {
var slice = Array.prototype.slice;
var args = slice.apply(arguments); // get arguments as args array
return function() {
return fn.apply(null, args.concat(slice.apply(arguments)));
};
}
return fn.apply(null, arguments);
};
}
// mul is just an example it can be any function....
function mul(x, y) {
console.log(x * y)
}
curry(mul)(11)(5)
This question makes me think of food .. but you can try this version:
const curry = (fn, ...args) =>
args.length < fn.length ? (...args2) => curry(fn, ...args, ...args2) : fn(...args);
const mul = (a, b, c) => console.log(a * b * c);
curry(mul)(3)(3)(3)
curry(mul)(3, 3)(3)
curry(mul, 3, 3, 3, 3) // extra parameters are ignored by mul
IE version here https://www.typescriptlang.org/play/index.html#src=const%20curry%20%3D%20(fn...
In python you can have a defaultdict(int) which stores int as values. And if you try to do a 'get' on a key which is not present in the dictionary you get zero as default value.
Can you do the same in javascript/jquery
You can build one using a JavaScript Proxy
var defaultDict = new Proxy({}, {
get: (target, name) => name in target ? target[name] : 0
})
This lets you use the same syntax as normal objects when accessing properties.
defaultDict.a = 1
console.log(defaultDict.a) // 1
console.log(defaultDict.b) // 0
To clean it up a bit, you can wrap this in a constructor function, or perhaps use the class syntax.
class DefaultDict {
constructor(defaultVal) {
return new Proxy({}, {
get: (target, name) => name in target ? target[name] : defaultVal
})
}
}
const counts = new DefaultDict(0)
console.log(counts.c) // 0
EDIT: The above implementation only works well with primitives. It should handle objects too by taking a constructor function for the default value. Here is an implementation that should work with primitives and constructor functions alike.
class DefaultDict {
constructor(defaultInit) {
return new Proxy({}, {
get: (target, name) => name in target ?
target[name] :
(target[name] = typeof defaultInit === 'function' ?
new defaultInit().valueOf() :
defaultInit)
})
}
}
const counts = new DefaultDict(Number)
counts.c++
console.log(counts.c) // 1
const lists = new DefaultDict(Array)
lists.men.push('bob')
lists.women.push('alice')
console.log(lists.men) // ['bob']
console.log(lists.women) // ['alice']
console.log(lists.nonbinary) // []
Check out pycollections.js:
var collections = require('pycollections');
var dd = new collections.DefaultDict(function(){return 0});
console.log(dd.get('missing')); // 0
dd.setOneNewValue(987, function(currentValue) {
return currentValue + 1;
});
console.log(dd.items()); // [[987, 1], ['missing', 0]]
I don't think there is the equivalent but you can always write your own. The equivalent of a dictionary in javascript would be an object so you can write it like so
function defaultDict() {
this.get = function (key) {
if (this.hasOwnProperty(key)) {
return key;
} else {
return 0;
}
}
}
Then call it like so
var myDict = new defaultDict();
myDict[1] = 2;
myDict.get(1);
A quick dirty hack can be constructed using Proxy
function dict(factory, origin) {
return new Proxy({ ...origin }, {
get(dict, key) {
// Ensure that "missed" keys are set into
// The dictionary with default values
if (!dict.hasOwnProperty(key)) {
dict[key] = factory()
}
return dict[key]
}
})
}
So the following code:
n = dict(Number, [[0, 1], [1, 2], [2, 4]])
// Zero is the default value mapped into 3
assert(n[3] == 0)
// The key must be present after calling factory
assert(Object.keys(n).length == 4)
Proxies definitely make the syntax most Python-like, and there's a library called defaultdict2 that offers what seems like a pretty crisp and thorough proxy-based implementation that supports nested/recursive defaultdicts, something I really value and am missing in the other answers so far in this thread.
That said, I tend to prefer keeping JS a bit more "vanilla"/"native" using a function-based approach like this proof-of-concept:
class DefaultMap {
constructor(defaultFn) {
this.defaultFn = defaultFn;
this.root = new Map();
}
put(...keys) {
let map = this.root;
for (const key of keys.slice(0, -1)) {
map.has(key) || map.set(key, new Map());
map = map.get(key);
}
const key = keys[keys.length-1];
map.has(key) || map.set(key, this.defaultFn());
return {
set: setterFn => map.set(key, setterFn(map.get(key))),
mutate: mutationFn => mutationFn(map.get(key)),
};
}
get(...keys) {
let map = this.root;
for (const key of keys) {
map = map?.get(key);
}
return map;
}
}
// Try it:
const dm = new DefaultMap(() => []);
dm.put("foo").mutate(v => v.push(1, 2, 3));
dm.put("foo").mutate(v => v.push(4, 5));
console.log(dm.get("foo")); // [1, 2, 3, 4, 5]
dm.put("bar", "baz").mutate(v => v.push("a", "b"));
console.log(dm.get("bar", "baz")); // ["a", "b"]
dm.put("bar", "baz").set(v => 42);
console.log(dm.get("bar", "baz")); // 42
dm.put("bar", "baz").set(v => v + 1);
console.log(dm.get("bar", "baz")); // 43
The constructor of DefaultMap accepts a function that returns a default value for leaf nodes. The basic operations for the structure are put and get, the latter of which is self-explanatory. put generates a chain of nested keys and returns a pair of functions that let you mutate or set the leaf node at the end of these keys. Accessing .root gives you the underlying Map structure.
Feel free to leave a comment if I've overlooked any bugs or miss useful features and I'll toss it in.
Inspired by #Andy Carlson's answer, here's an implementation that works in a slightly more Pythonic way:
class DefaultDict {
constructor(defaultVal) {
return new Proxy(
{},
{
get: (target, name) => {
if (name == '__dict__') {
return target;
} else if (name in target) {
return target[name];
} else {
target[name] = defaultVal;
return defaultVal;
}
},
}
);
}
}
Basically, it also lets you retrieve all the gotten and set values of the "target", similar to how collections.defaultdict works in Python. This allows us to do things like:
const myDict = new DefaultDict(0);
myDict['a'] += 1;
myDict['b'] += 2;
myDict['c'] += 3;
myDict['whatever'];
console.log(myDict.__dict__);
// {'a': 1, 'b': 2, 'c': 3, 'whatever': 0}
To add to Andy Carlson's answer
If you default dict an array, you'll get a toJSON field in the resulting object. You can get rid of it by deconstructing to a new object.
const dd = new DefaultDict(Array);
//...populate the dict
return {...dd};
The original answer does not seem to work on the nested cases. I made some modifications to make it work:
class DefaultDict {
constructor(defaultInit) {
this.original = defaultInit;
return new Proxy({}, {
get: function (target, name) {
if (name in target) {
return target[name];
} else {
if (typeof defaultInit === "function") {
target[name] = new defaultInit().valueOf();
} else if (typeof defaultInit === "object") {
if (typeof defaultInit.original !== "undefined") {
target[name] = new DefaultDict(defaultInit.original);
} else {
target[name] = JSON.parse(JSON.stringify(defaultInit));
}
} else {
target[name] = defaultInit;
}
return target[name];
}
}
});
}
}
var a = new DefaultDict(Array);
a["banana"].push("ya");
var b = new DefaultDict(new DefaultDict(Array));
b["orange"]["apple"].push("yo");
var c = new DefaultDict(Number);
c["banana"] = 1;
var d = new DefaultDict([2]);
d["banana"].push(1);
var e = new DefaultDict(new DefaultDict(2));
e["orange"]["apple"] = 3;
var f = new DefaultDict(1);
f["banana"] = 2;
The difference is that if defaultInit is an object, we need to return a deep copy of the object, instead of the original one.
I want to create a dictionary in JavaScript like the following:
myMappings = [
{ "Name": 10%},
{ "Phone": 10%},
{ "Address": 50%},
{ "Zip": 10%},
{ "Comments": 20%}
]
I want to populate an HTML table later and want to set the titles of table to the first column of myMappings and the width of columns to the second. Is there a clean way to do it?
Another approach would be to have an array of objects, with each individual object holding the properties of a column. This slightly changes the structure of "myMappings", but makes it easy to work with:
var myMappings = [
{ title: "Name", width: "10%" },
{ title: "Phone", width: "10%" },
{ title: "Address", width: "50%" },
{ title: "Zip", width: "10%" },
{ title: "Comments", width: "20%" }
];
Then you could easily iterate through all your "columns" with a for loop:
for (var i = 0; i < myMappings.length; i += 1) {
// myMappings[i].title ...
// myMappings[i].width ...
}
The main problem I see with what you have is that it's difficult to loop through, for populating a table.
Simply use an array of arrays:
var myMappings = [
["Name", "10%"], // Note the quotes around "10%"
["Phone", "10%"],
// etc..
];
... which simplifies access:
myMappings[0][0]; // column name
myMappings[0][1]; // column width
Alternatively:
var myMappings = {
names: ["Name", "Phone", etc...],
widths: ["10%", "10%", etc...]
};
And access with:
myMappings.names[0];
myMappings.widths[0];
An object technically is a dictionary.
var myMappings = {
mykey1: 'myValue',
mykey2: 'myValue'
};
var myVal = myMappings['myKey1'];
alert(myVal); // myValue
You can even loop through one.
for(var key in myMappings) {
var myVal = myMappings[key];
alert(myVal);
}
There is no reason whatsoever to reinvent the wheel. And of course, assignment goes like:
myMappings['mykey3'] = 'my value';
And ContainsKey:
if (myMappings.hasOwnProperty('myKey3')) {
alert('key already exists!');
}
I suggest you follow this: http://javascriptissexy.com/how-to-learn-javascript-properly/
You may be trying to use a JSON object:
var myMappings = { "name": "10%", "phone": "10%", "address": "50%", etc.. }
To access:
myMappings.name;
myMappings.phone;
etc..
Try:
var myMappings = {
"Name": "10%",
"Phone": "10%",
"Address": "50%",
"Zip": "10%",
"Comments": "20%"
}
// Access is like this
myMappings["Name"] // Returns "10%"
myMappings.Name // The same thing as above
// To loop through...
for(var title in myMappings) {
// Do whatever with myMappings[title]
}
I suggest not using an array unless you have multiple objects to consider. There isn't anything wrong this statement:
var myMappings = {
"Name": 0.1,
"Phone": 0.1,
"Address": 0.5,
"Zip": 0.1,
"Comments": 0.2
};
for (var col in myMappings) {
alert((myMappings[col] * 100) + "%");
}
An easier, native and more efficient way of emulating a dict in JavaScript than a hash table:
It also exploits that JavaScript is weakly typed. Rather type inference.
Here's how (an excerpt from Google Chrome's console):
var myDict = {};
myDict.one = 1;
1
myDict.two = 2;
2
if (myDict.hasOwnProperty('three'))
{
console.log(myDict.two);
}
else
{
console.log('Key does not exist!');
}
Key does not exist! VM361:8
if (myDict.hasOwnProperty('two'))
{
console.log(myDict.two);
}
else
{
console.log('Key does not exist!');
}
2 VM362:4
Object.keys(myDict);
["one", "two"]
delete(myDict.two);
true
myDict.hasOwnProperty('two');
false
myDict.two
undefined
myDict.one
1
Here's a dictionary that will take any type of key as long as the toString() property returns unique values. The dictionary uses anything as the value for the key value pair.
See Would JavaScript Benefit from a Dictionary Object.
To use the dictionary as is:
var dictFact = new Dict();
var myDict = dictFact.New();
myDict.addOrUpdate("key1", "Value1");
myDict.addOrUpdate("key2", "Value2");
myDict.addOrUpdate("keyN", "ValueN");
The dictionary code is below:
/*
* Dictionary Factory Object
* Holds common object functions. similar to V-Table
* this.New() used to create new dictionary objects
* Uses Object.defineProperties so won't work on older browsers.
* Browser Compatibility (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties)
* Firefox (Gecko) 4.0 (2), Chrome 5, Internet Explorer 9, Opera 11.60, Safari 5
*/
function Dict() {
/*
* Create a new Dictionary
*/
this.New = function () {
return new dict();
};
/*
* Return argument f if it is a function otherwise return undefined
*/
function ensureF(f) {
if (isFunct(f)) {
return f;
}
}
function isFunct(f) {
return (typeof f == "function");
}
/*
* Add a "_" as first character just to be sure valid property name
*/
function makeKey(k) {
return "_" + k;
};
/*
* Key Value Pair object - held in array
*/
function newkvp(key, value) {
return {
key: key,
value: value,
toString: function () { return this.key; },
valueOf: function () { return this.key; }
};
};
/*
* Return the current set of keys.
*/
function keys(a) {
// remove the leading "-" character from the keys
return a.map(function (e) { return e.key.substr(1); });
// Alternative: Requires Opera 12 vs. 11.60
// -- Must pass the internal object instead of the array
// -- Still need to remove the leading "-" to return user key values
// Object.keys(o).map(function (e) { return e.key.substr(1); });
};
/*
* Return the current set of values.
*/
function values(a) {
return a.map(function(e) { return e.value; } );
};
/*
* Return the current set of key value pairs.
*/
function kvPs(a) {
// Remove the leading "-" character from the keys
return a.map(function (e) { return newkvp(e.key.substr(1), e.value); });
}
/*
* Returns true if key exists in the dictionary.
* k - Key to check (with the leading "_" character)
*/
function exists(k, o) {
return o.hasOwnProperty(k);
}
/*
* Array Map implementation
*/
function map(a, f) {
if (!isFunct(f)) { return; }
return a.map(function (e, i) { return f(e.value, i); });
}
/*
* Array Every implementation
*/
function every(a, f) {
if (!isFunct(f)) { return; }
return a.every(function (e, i) { return f(e.value, i) });
}
/*
* Returns subset of "values" where function "f" returns true for the "value"
*/
function filter(a, f) {
if (!isFunct(f)) {return; }
var ret = a.filter(function (e, i) { return f(e.value, i); });
// if anything returned by array.filter, then get the "values" from the key value pairs
if (ret && ret.length > 0) {
ret = values(ret);
}
return ret;
}
/*
* Array Reverse implementation
*/
function reverse(a, o) {
a.reverse();
reindex(a, o, 0);
}
/**
* Randomize array element order in-place.
* Using Fisher-Yates shuffle algorithm.
*/
function shuffle(a, o) {
var j, t;
for (var i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
t = a[i];
a[i] = a[j];
a[j] = t;
}
reindex(a, o, 0);
return a;
}
/*
* Array Some implementation
*/
function some(a, f) {
if (!isFunct(f)) { return; }
return a.some(function (e, i) { return f(e.value, i) });
}
/*
* Sort the dictionary. Sorts the array and reindexes the object.
* a - dictionary array
* o - dictionary object
* sf - dictionary default sort function (can be undefined)
* f - sort method sort function argument (can be undefined)
*/
function sort(a, o, sf, f) {
var sf1 = f || sf; // sort function method used if not undefined
// if there is a customer sort function, use it
if (isFunct(sf1)) {
a.sort(function (e1, e2) { return sf1(e1.value, e2.value); });
}
else {
// sort by key values
a.sort();
}
// reindex - adds O(n) to perf
reindex(a, o, 0);
// return sorted values (not entire array)
// adds O(n) to perf
return values(a);
};
/*
* forEach iteration of "values"
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEach(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for(var i = 0; i < a.length; i++) {
if (f(a[i].value, i)) { break; }
}
};
/*
* forEachR iteration of "values" in reverse order
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEachR(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for (var i = a.length - 1; i > -1; i--) {
if (f(a[i].value, i)) { break; }
}
}
/*
* Add a new Key Value Pair, or update the value of an existing key value pair
*/
function add(key, value, a, o, resort, sf) {
var k = makeKey(key);
// Update value if key exists
if (exists(k, o)) {
a[o[k]].value = value;
}
else {
// Add a new Key value Pair
var kvp = newkvp(k, value);
o[kvp.key] = a.length;
a.push(kvp);
}
// resort if requested
if (resort) { sort(a, o, sf); }
};
/*
* Removes an existing key value pair and returns the "value" If the key does not exists, returns undefined
*/
function remove(key, a, o) {
var k = makeKey(key);
// return undefined if the key does not exist
if (!exists(k, o)) { return; }
// get the array index
var i = o[k];
// get the key value pair
var ret = a[i];
// remove the array element
a.splice(i, 1);
// remove the object property
delete o[k];
// reindex the object properties from the remove element to end of the array
reindex(a, o, i);
// return the removed value
return ret.value;
};
/*
* Returns true if key exists in the dictionary.
* k - Key to check (without the leading "_" character)
*/
function keyExists(k, o) {
return exists(makeKey(k), o);
};
/*
* Returns value assocated with "key". Returns undefined if key not found
*/
function item(key, a, o) {
var k = makeKey(key);
if (exists(k, o)) {
return a[o[k]].value;
}
}
/*
* changes index values held by object properties to match the array index location
* Called after sorting or removing
*/
function reindex(a, o, i){
for (var j = i; j < a.length; j++) {
o[a[j].key] = j;
}
}
/*
* The "real dictionary"
*/
function dict() {
var _a = [];
var _o = {};
var _sortF;
Object.defineProperties(this, {
"length": { get: function () { return _a.length; }, enumerable: true },
"keys": { get: function() { return keys(_a); }, enumerable: true },
"values": { get: function() { return values(_a); }, enumerable: true },
"keyValuePairs": { get: function() { return kvPs(_a); }, enumerable: true},
"sortFunction": { get: function() { return _sortF; }, set: function(funct) { _sortF = ensureF(funct); }, enumerable: true }
});
// Array Methods - Only modification to not pass the actual array to the callback function
this.map = function(funct) { return map(_a, funct); };
this.every = function(funct) { return every(_a, funct); };
this.filter = function(funct) { return filter(_a, funct); };
this.reverse = function() { reverse(_a, _o); };
this.shuffle = function () { return shuffle(_a, _o); };
this.some = function(funct) { return some(_a, funct); };
this.sort = function(funct) { return sort(_a, _o, _sortF, funct); };
// Array Methods - Modified aborts when funct returns true.
this.forEach = function (funct) { forEach(_a, funct) };
// forEach in reverse order
this.forEachRev = function (funct) { forEachR(_a, funct) };
// Dictionary Methods
this.addOrUpdate = function(key, value, resort) { return add(key, value, _a, _o, resort, _sortF); };
this.remove = function(key) { return remove(key, _a, _o); };
this.exists = function(key) { return keyExists(key, _o); };
this.item = function(key) { return item(key, _a, _o); };
this.get = function (index) { if (index > -1 && index < _a.length) { return _a[index].value; } } ,
this.clear = function() { _a = []; _o = {}; };
return this;
}
return this;
}