How to use two integers as a key - javascript

I have a memoization problem. There is a value calculated between two objects and I want to store it, such that I can pass in the two number id's from the objects to get the value regardless of the order that I pass in the id's.
I could just store it under both of the id's, or check for both id's, but I was wondering out of curiosity if there was a more efficient solution.
Examples:
Check for both id's:
var storedValues = {};
function generateValue(a, b)
{
if (storedValues[a.id][b.id])
return storedValues[a.id][b.id];
else if (storedValues[b.id][a.id])
return storedValues[b.id][a.id];
else
// Generate value, store it under a.id and return it
}
Store it under both id's:
var storedValues = {};
function generateValue(a, b)
{
if (storedValues[a.id][b.id])
return storedValues[a.id][b.id];
else
// Generate value, store it under a.id and b.id and return it
}
How can I generate a unique map key from two numbers, so I can retrieve the value with those two numbers regardless of the order that I pass them in?
I'm using JavaScript btw.

There are many ways to that, first of all, you can just create a single key for the objectkey e.g.: '11_12'
var storedValues= {};
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
var objectkey = id1+'_'+id2;
storedValues[objectkey] = calculatedvalue;
it is also possible to use nested object:
var storedValues= {};
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
if (storedValues[id1] == null ){
storedValues[id1] = {};
}
storedValues[id1][id2] = calculatedvalue;
More over your ids are always integer you can use Arrays
var storedValuesArr = [];
var id1 = 11;
var id2 = 12;
var calculatedvalue = id1 * id2;
if (!Array.isArray(storedValuesArr[id1])){
storedValuesArr[id1] = [];
}
storedValuesArr[id1][id2] = calculatedvalue;
and in order to return the values regardless of the order:
function generateValue(a, b)
{
if (storedValues[a.id] != null && storedValues[a.id][b.id] !== null)
return storedValues[a.id][b.id]
else if (storedValues[b.id] != null && storedValues[b.id][a.id] !== null)
return storedValues[b.id][a.id];
else
// Generate value, store it under a.id and return it
}

You could sort the keys for setting the value and for getting the value.
function getValue(object, key0, key1) {
return [key0, key1]
.sort((a, b) => a - b)
.reduce((o, k) => (o || {})[k], object);
}
function setValue(object, key0, key1, value) {
var keys = [key0, key1].sort((a, b) => a - b),
last = keys.pop();
object[keys[0]] = object[keys[0]] || {};
object[keys[0]][last] = value;
}
var object = {};
setValue(object, 3, 7, 42);
console.log(getValue(object, 3, 7));
console.log(getValue(object, 7, 3));
console.log(object);
Different approach by using the keys a given.
function getValue(object, key0, key1) {
return key0 in object ? object[key0][key1] : object[key1][key0];
}
function setValue(object, key0, key1, value) {
object[key0] = object[key0] || {};
object[key0][key1] = value;
}
var object = {};
setValue(object, 3, 7, 42);
console.log(getValue(object, 3, 7));
console.log(getValue(object, 7, 3));
console.log(object);

Related

Delete all occurrence of duplicate values in an array in javascript/jquery

I am trying to work on a problem where I want to remove all the occurrences of similar value in an array
eg.
var sampleArr = ["mary","jane","spiderman","jane","peter"];
and I am trying to get result as => ["marry","spiderman","peter"]
how do I get this?
You can use filter()
var arr = ["mary","jane","spiderman","jane","peter"];
var unique = arr.filter((x, i) => arr.indexOf(x) === i);
console.log(unique);
var sampleArr = ["mary","jane","spiderman","jane","peter"];
var unique = [];
var itemCount = {};
sampleArr.forEach((item,index)=>{
if(!itemCount[item]){
itemCount[item] = 1;
}
else{
itemCount[item]++;
}
});
for(var prop in itemCount){
if(itemCount[prop]==1){
unique.push(prop);
}
}
console.log(unique);
Check this.
You can count the frequency of the character and just pick the character whose frequency is 1.
const arr = ["mary","jane","spiderman","jane","peter"];
frequency = arr.reduce((o,ch) => {
o[ch] = (o[ch] || 0) + 1;
return o;
}, {}),
unique = Object.keys(frequency).reduce((r,k) => frequency[k] === 1? [...r, k]: r, []);
console.log(unique);
You can use filter:
var sampleArr = ["mary","jane","spiderman","jane","peter"];
var result = sampleArr.filter((e,_,s)=>s.filter(o=>o==e).length<2);
// or with reduce and flatmap
const result1 = Object.entries(sampleArr.reduce((a,e)=>(a[e]??=0, a[e]++, a),{})).flatMap(([k,v])=>v==1 ? k: []);
console.log(result)
console.log(result1);
lot of solution, here easy solution to understand using match to have the occurence and filter to eliminate:
var arr = ['ab','pq','mn','ab','mn','ab'];
var st = arr.join(",");
result = arr.filter(it => {
let reg = new RegExp(it, 'g');
return st.match(reg).length == 1;
});
console.log(result);// here ["pq"]
filter seems to be your best bet here if you need extremely robust performance. No real need for jQuery in this instance. Personally, I would opt for readability for something like this and instead sort, set duplicates to null, and then remove all null values.
var sampleArr = ["mary","jane","spiderman","jane","peter"];
var result = sampleArr.sort().forEach((value, index, arr) => {
// if the next one is the same value,
// set this one to null
if(value === arr[value + 1])
return arr[index] = null;
// if the previous one is null, and the next one is different,
// this is the last duplicate in a series, so should be set to null
if(arr[value - 1] === arr[index + 1] !== value)
return arr[index] = null;
return
})
.filter(value => value === null) //remove all null values
console.log(result);

javascript - filter array of strings based on seperated ending

I have an array of strings like this:
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
And I want to filter them so that only the ones that have the latest versions for the given "author:name" are left, thus removing ones that are not the latest (i.e. the "1.0.1" ones).
My expected result is this:
const filteredStrings = [
"author:app:1.0.1",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
Any way to do this simply?
You can do it with two loops first one find new ones second one check which is bigger
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
filteredones = [];
strings.forEach(element => {
var arr = element.split(":");
var isnew = true;
var found = filteredones.find(function(element2) {
var x = element2.split(":");
return x[1] == arr[1] && x[0] == arr[0]
});
if (found == undefined) {
filteredones.push(element);
}
});
for (var i = 0; i < filteredones.length; i++) {
element = filteredones[i];
var arr = element.split(":");
var isnew = true;
var found = strings.find(function(element2) {
var x = element2.split(":");
return x[1] == arr[1] && x[0] == arr[0] && x[2] > arr[2]
});
if (found != undefined) {
filteredones[i] = found;
}
};
console.log(filteredones);
you can check the value in the last index of the string in each of the elements of the array and if they qualify as a latest one put it to a new array.
You can use an object to store the key/version pairs, and convert to appropriate output on the end. The version comparison can be any of those found here: How to compare software version number using js? (only number)
result = {};
for (var s of input) {
// parts = ["author", "appname", "version"]
var parts = s.split(":");
var i = parts[0] + ":" + parts[1];
if (!result[i] || compareVersion(parts[2], result[i]))
// If not present or version is greater
result[i] = parts[2]; // Add to result
}
result = Object.keys(result).map(k => k + ":" + result[k])
Working demo: https://codepen.io/bortao/pen/LYVmagK
Build an object with keys as app name.
getValue method is calculate the version value so that to compare.
Update object value, when you see the version is recent (value is big).
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
const filter = data => {
const res = {};
const getValue = item =>
item
.split(":")[2]
.split(".")
.reduceRight((acc, curr, i) => acc + curr * Math.pow(10, i), 0);
data.forEach(item => {
const app = item
.split(":")
.slice(0, 2)
.join(":");
if (!res[app] || (app in res && getValue(item) > getValue(res[app]))) {
res[app] = item;
}
});
return Object.values(res);
};
console.log(filter(strings));

How to return element out of array using filter method?

This code returns an array of arrays:
var values = [[1],["a"],,["b"],[""],["c"]];
var noBlankValues = values.filter(function (el) {
var v = el != null && el != "";
return v;
});
console.log(noBlankValues);
How should I change it to get an array like [1,"a","b","c"]?
I tried this but with no luck:
var values = [[1],["a"],,["b"],[""],["c"]];
var noBlankValues = values.filter(function (el) {
var v = el != null && el != "";
return v[0];
});
console.log(noBlankValues);
You could map array items and then filter for empty strings.
var values = [[1], ["a"], , ["b"], [""], ["c"]],
result = values
.map(([v]) => v)
.filter(v => v !== '');
console.log(result);
ES5
var values = [[1], ["a"], , ["b"], [""], ["c"]],
result = values
.map(function (v) { return v[0]; })
.filter(function (v) { return v !== ''; });
console.log(result);
You can use flat and filter like this. The flat method also removes holes in arrays (MDN)
const values = [[1],["a"],,["b"],[""],["c"]];
const noBlankValues = values.flat().filter(a => a);
console.log(noBlankValues);
The above filter removes all the falsy values. If you specifically want to remove null and empty strings:
const values = [[1],["a"],,["b"],[""],["c"]];
const noBlankValues = values.flat().filter(a => a !== null && a !== "");
console.log(noBlankValues);
Note: Browser compatibility for flat()
It is worth noting that, filter function requires a boolean return MSDN
This will filter the values first, giving you the array of no blanks.
values = [[1],["a"],,["b"],[""],["c"]];
noBlankValues = values.filter(function (el) {
return el[0] != null && el[0] != "";
});
console.log(noBlankValues);
You can the loop through the array using the map function to get the items directly in the main array.
values = [[1],["a"],,["b"],[""],["c"]];
noBlankValues = values.filter(function (el) {
return el[0] != null && el[0] != "";
}).map(function(item) {
return item[0];
});
console.log(noBlankValues);

How to remove array from an array in javascript (Angular6)?

I have this array
var a = [[1,3][2,3],[3,4],[6,7]];
var b =[[1,3],[2,3]];
I want to remove b array from a.
I tried it
var arr = [[1,3],[2,3]];
var b = [1,3];
var arr = arr.filter((el)=> !b.includes(el));
console.log(arr);
but it is returning the same array "arr". It's not removing the array "b" form array "arr".
In case of simplistic use case, where the order of items, number of items, value of items will be same you can try following
var a = [[1,3], [2,3]];
var b =[1,3];
a = a.filter(v=> v.join() !== b.join());
console.log(a);
If you want to consider order, number and value, please try following
var a = [[1,3], [2,3]];
var b =[1,3];
// Create a map where key is item and value is its occurence
let bObj = b.reduce((ac, c) => Object.assign(ac, {[c]: (ac[c] || 0) + 1}), {});
a = a.filter(v=> {
// For every value, create a copy of bObj
let temp = {...bObj};
// for every value in item, decrements the value by 1
v.forEach(e => temp[e] = (temp[e] || 0) - 1);
// Get all the values of object
let tempValues = Object.values(temp);
// If all values are 0, then it is an exact match, return false else return true
if(new Set(tempValues).size === 1 && tempValues[0] === 0) return false;
return true;
});
console.log(a);
EDIT
var a = [[1,3],[2,3],[3,4],[6,7]];
var b = [[1,3],[2,3]];
// Create a map where key is item and value is its occurence
let bObj = b.map(v => v.reduce((ac, c) => Object.assign(ac, {[c]: (ac[c] || 0) + 1}), {}));
a = a.filter(v=> {
// For every value, create a copy of bObj
let temp = JSON.parse(JSON.stringify(bObj));
// for every value in item, decrements the value by 1
v.forEach(e => temp.forEach(t => t[e] = (t[e] || 0) - 1));
return !temp.some(t => {
// Get all the values of object
let tempValues = Object.values(t);
// If all values are 0, then it is an exact match, return false else return true
return (new Set(tempValues).size === 1 && tempValues[0] === 0)
});
});
console.log(a);
You could search fr the index by stringify the inner array and check against the strigified array b. Then check the index and splice.
var a = [[1, 3], [2, 3]],
b = [1, 3],
index = a.findIndex(
(json => inner => JSON.stringify(inner) === json)
(JSON.stringify(b))
);
if (index !== -1) {
a.splice(index, 1);
}
console.log(a);
You can first find the index on arr that you want to remove using every() operation then splice() it:
var arr = [[1,3],[2,3]];
var b = [1,3];
var index = arr.findIndex(item => item.every(elem => b.includes(elem)));
arr.splice(index,1);
console.log(arr);
var my_array = ["raj","bob","clreak","kevin","dJ"];
var start_index = 0
var number_of_elements_to_remove = 1;
var removed_elements = my_array.splice(start_index, number_of_elements_to_remove);

Find object by properties from array

With an array, a value, and and an object with nested objects:
Object
mesh
Array
['options', 'range', 'x']
Value
12.5
Is it possible to translate this to update a property, e.g.
mesh.options.range.x = 12.5
Attempted:
index = (obj, i) ->
obj[i]
arr.reduce(index, obj) = 12.5
Update
Thank you all for the elegant solutions.
Using .reduce() is actually pretty nice for this:
// current object----| |----current key
// v v
arr.reduce(function(obj, key) {
return obj == null ? obj : obj[key];
}, window.mesh);
// ^
// |-- initial object
Your attempt to use .reduce() needed to pass a function that manages the "accumulation".
Here, as long as the previous obj wasn't null or undefined, it'll return the key of the current obj, which becomes the next obj.
Then since you need to assign a value, you'd actually want to get the value of the second to last key.
var o = arr.slice(0,-1).reduce(function(obj, key) {
return obj == null ? obj : obj[key];
}, window.mesh);
And then check its existence and use the last item in arr to do the assignment.
o && o[arr.pop()] = 12.5;
All of this can be abstracted away into a function that does one or the other based on how many arguments were passed.
function setFromArray(obj, arr, val) {
var keys = arguments.length < 3 ? arr.slice() : arr.slice(0, -1);
var o = keys.slice(0,-1).reduce(function(obj, key) {
return obj == null ? obj : obj[key];
}, window.mesh);
if (arguments.length < 3)
return o;
else
o && o[keys.pop()];
}
Here's a general solution:
function setPropertyPath(obj, path, value) {
var o = obj;
for (var i = 0; i < path.length - 1; i++) {
o = o[path[i]];
}
o[path[path.length - 1]] = value;
}
Usage:
var obj = { a: { b: { c: 0 } } };
setPropertyPath(obj, ['a', 'b', 'c'], 10);
console.log(obj.a.b.c); // prints '10'
JSBin
var mesh = {},
arr = ['options','range','x'],
value = 12.5;
mesh[arr[0]][arr[1]][arr[2]] = value;
If array length is static do something like this:
mesh[array[0]][array[1]][array[2]] = value;
However, one problem with this is that javascript doesn't do autovivification, so if you're accessing a key value that isn't previously defined you could run into errors (if mesh.options hasn't been defined then the above will throw an error because you can't assign to it). To solve that you might abstract this out into a function that handles things recursively:
http://jsfiddle.net/h4jVg/
function update_val(obj, array, val, prev) {
if (array.length == 0) {
obj = val;
return;
}
var cur = array.shift();
if(array.length == 0) {
obj[cur] = val;
return;
} else if (obj[cur] == undefined) {
obj[cur] = {};
}
update_val(obj[cur], array, val);
}

Categories

Resources