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);
Related
It is a simple exercise that I am doing for mere practice and leisure, I have done it in various ways but I was wondering if there is an even more practical way or to reduce the lines of code making use of the many methods of JavaScript.
The exercise is about receiving an array (arr) and a number (target) and returning another array with a pair of numbers found in 'arr' whose sum is equal to 'target'.
function targetSum3(arr, target) {
let newArr = [];
let copyArray = arr;
for (let i of copyArray) {
let x = Math.abs(i - target);
copyArray.pop(copyArray[i]);
if (copyArray.includes(x) && (copyArray.indexOf(x) != copyArray.indexOf(i))) {
newArr.push(i);
newArr.push(x);
return newArr;
}
}
return newArr;
}
If you are fine with a function that just returns a pair of numbers (the first match so to speak) whose sum equals the targets value, this might be enough:
function sumPair (arr, target) {
while(arr.length) {
let sum1 = arr.shift();
let sum2 = arr.find(val => sum1 + val === target);
if (sum2) return [sum2, sum1];
}
return null;
}
const targetSum = (arr, target) => {
const first = arr.find((v,i,a) => arr.includes(target-v) && (arr.indexOf(target-v) !== i));
return first ? [first, target - first] : null;
};
const values = [1,2,3,4,5,6,7,8,9];
console.log(targetSum(values, 1)); // null
console.log(targetSum(values, 2)); // null
console.log(targetSum(values, 3)); // [1, 2]
console.log(targetSum(values, 15)); // [6, 9]
console.log(targetSum(values, 20)); // null
I changed for loop with forEach (more efficient) and there is no need for the copyArray array so I removed it. I also changed pop() with shift(), I think you want to shift the array and not pop-it (if I understand the task correctly).
function targetSum3(arr, target) {
let newArr = [];
arr.forEach(element => {
let x = Math.abs(element - target); // calc x
arr.shift(); // removes first element from arr (current element)
if (arr.includes(x) && (arr.indexOf(x) != arr.indexOf(element))) {
newArr.push(element);
newArr.push(x);
return;
}
});
return newArr;
}
use Array.filter to find the target sum for all values in an given array. See comments in the snippet.
sumsForTargetInArray();
document.addEventListener(`click`,
evt => evt.target.id === `redo` && sumsForTargetInArray());
function sumsInArray(arr, target) {
// clone the array
const clone = arr.slice();
let result = [];
while (clone.length) {
// retrieve the current value (shifting it from the clone)
const current = clone.shift();
// filter arr: all values where value + sum = target
const isTarget = arr.filter(v => current + v === target);
// add to result.
// Sorting is to prevent duplicates later
if (isTarget.length) {
result = [...result, ...isTarget.map(v => [current, v].sort())];
}
}
// weed out duplicates (e.g. 0 + 3, 3 + 0)
const unique = new Set();
result.forEach(r => unique.add(`${r[0]},${r[1]}`));
// return array of array(2)
return [...unique].map(v => v.split(`,`).map(Number));
}
function sumsForTargetInArray() {
const testArr = [...Array(20)].map((_, i) => i);
const target = Math.floor(Math.random() * 30);
document.querySelector(`pre`).textContent = `testArray: ${
JSON.stringify(testArr)}\ntarget: ${target}\nResult: ${
JSON.stringify(sumsInArray(testArr, target))}`;
}
<pre></pre>
<button id="redo">Again</button>
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);
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);
I have an array like this:
var arr = [a,a,b,b,b,c]
The result(a new array) should only show all the values which are exactly 2 times in this array, e.g.: a
Do you guys know how I could realize this? Thanks
Please try this code:
var arr = ['a','a','b','b','b','c'];
var numberOfOccurrences = {};
arr.forEach(function (item) {
numberOfOccurrences[item] = (numberOfOccurrences[item] || 0) + 1;
});
var duplicates = Object.keys(numberOfOccurrences).filter(function (item) { return numberOfOccurrences[item] === 2 });
console.log(duplicates);
You can first create object and add properties with forEach loop and then use filter on Object.keys to return array as result.
var arr = ['a','a','b','b','b','c'];
var o = {}
arr.forEach(e => o[e] = (o[e] || 0) + 1);
var result = Object.keys(o).filter(e => o[e] == 2);
console.log(result)
Trying to get two data sets to intersect but I can't do it. For example, in my code below, intersecting mySet and mySet2 should yield "1" since they both have a value of "1" in their set.
var mySet = new Set();
var mySet2=new Set();
mySet.add(1);
mySet.add(2);
mySet.add("HELLOOOOO");
mySet2.add("hi");
mySet2.add(1);
var a = Array(mySet, mySet2);
console.log(a);
mySet.forEach(function(value) {
console.log(value);
});
mySet2.forEach(function(value) {
console.log(value);
});
function intersection_destructive(a, b)
{
var result = new Array();
while( mySet.length > 0 && mySet2.length > 0 )
{
if (mySet[0] < mySet2[0] ){ mySet.shift(); }
else if (mySet[0] > mySet2[0] ){ mySet2.shift(); }
else /* they're equal */
{
result.push(mySet.shift());
mySet2.shift();
}
}
return result;
}
Set 1 and Set 2 both have "1" in it but my function (intersection_destructive) doesn't return it. I'm not sure how to intersect them, I searched stackoverflow and found intersection_destructive but it didn't work for me, I also tried:
array1.filter(function(n) {
return array2.indexOf(n) != -1
});
as per this: Simplest code for array intersection in javascript
but I get an error on filter when I try to run it.
Sadly as you've figured out there are no native intersection or union operations. It's not terribly complex to find the intersection though:
let a = new Set([1,2,3])
let b = new Set([1,2,4])
let intersect = new Set([...a].filter(i => b.has(i)));
console.log(...intersect)
To get the intersection, you can iterate the items of a set and check if they belong to the other one:
var intersect = new Set();
for(var x of mySet1) if(mySet2.has(x)) intersect.add(x);
In ES7 you can simplify it with array comprehensions or generator comprehensions:
var intersect = new Set((for (x of mySet1) if (mySet2.has(x)) x));
You have been trying array intersections methods. You cannot use them on ES6 sets. Instead, use
function intersect(...sets) {
if (!sets.length) return new Set();
const i = sets.reduce((m, s, i) => s.size < sets[m].size ? i : m, 0);
const [smallest] = sets.splice(i, 1);
const res = new Set();
for (let val of smallest)
if (sets.every(s => s.has(val)))
res.add(val);
return res;
}
One technique for intersecting sets is to take the smallest set and check for each of its values in the other sets, removing the value if any set does not contain it. Although runtime is O(n x m), where n is the set length and m is the number of sets, the n is likely to be much smaller if the sets are not all the same length.
function setsIntersection(...sets) {
if (sets.length < 1) {
return new Set();
}
let min_size = sets[0].size;
let min_set_index = 0;
for (let i = 1; i < sets.length; i++) {
const size = sets[i].size;
if (size < min_size) {
min_size = size;
min_set_index = i;
}
}
const temp_swap_set = sets[0];
sets[0] = sets[min_set_index];
sets[min_set_index] = temp_swap_set;
const result = new Set(sets[min_set_index]);
for (let i = 1; i < sets.length; i++) {
for (const v of result) {
if (!sets[i].has(v)) {
result.delete(v);
}
}
}
return result;
}
You could iterate the set and create a new set for the common values.
const
intersectSets = (a, b) => {
const c = new Set;
a.forEach(v => b.has(v) && c.add(v));
return c;
},
a = new Set([1, 2, 3]),
b = new Set([1, 2, 4]),
c = new Set([1, 2, 4, 5]),
n = new Set([1, 2]);
console.log(...[a, b, c, n].reduce(intersectSets));
This will work on both Sets and Arrays. It will return whatever the type of set1 is. Arguments can be mixed types.
/**
* Returns the intersection of ES6 Set objects. i.e., returns a new set with only elements contained in all the given sets.
*
* #param {Set|Array} set1 First set
* #param {Array<Array|Set>} sets Other sets
* #returns {Set|Array} Intersection
*/
export function intersection(set1, ...sets) {
if(!sets.length) {
return set1;
}
const tmp = [...set1].filter(x => sets.every(y => Array.isArray(y) ? y.includes(x) : y.has(x)));
return Array.isArray(set1) ? tmp : new Set(tmp);
}
I built it for working with Sets, but then I realized it's probably more expensive to convert an array into a set just so I can use .has on it once than to just use .includes.
With the currently Stage 3 proposal https://github.com/tc39/proposal-set-methods, you could use:
mySet.intersection(mySet2);
const set1 = new Set([1, 2, 3]);
const set2 = new Set([1, 2, 4]);
const intersect = set1.intersection(set2);
console.log(...intersect);
<script src="https://unpkg.com/core-js-bundle#3.27.1/minified.js"></script>
Until then, you could use Immutable.js's Set, which inspired that proposal:
Immutable.Set(mySet).intersect(mySet2);
From the example: intersection_destructive takes 2 ARRAYS not 2 SETS. Here is your code working with the intersection_destructive example.
// These must be sorted arrays!
var mySet = [1,2,6];
var mySet2 = [1,2,5];
// Takes 2 Arrays
// array properties: shift
function intersection_destructive(a, b)
{
var result = [];
while( a.length > 0 && b.length > 0 )
{
if (a[0] < b[0] ){ b.shift(); }
else if (a[0] > b[0] ){ b.shift(); }
else /* they're equal */
{
result.push(a.shift());
b.shift();
}
}
return result;
};
var result = intersection_destructive(mySet, mySet2);
console.log(result); // Output: [1,2]
To use the Array.filter() is a useful pattern. You need to convert your sets to array's using Array.from(). Also make sure that you filter through smaller set into the bigger one.
see fiddle
var mySet = new Set();
var mySet2=new Set();
mySet.add(1);
mySet.add(2);
mySet.add("HELLOOOOO");
mySet2.add("hi");
mySet2.add(1);
console.log(intersection_destructive(mySet, mySet2));
function intersection_destructive(a, b)
{
// filter over the shorter set and convert sets to Array's so we have filter
if (a.length > b.length) {
var array1 = Array.from(a);
var array2 = Array.from(b);
} else {
var array1 = Array.from(b);
var array2 = Array.from(a);
}
var result = array1.filter(function(n) {
return array2.indexOf(n) != -1
});
return result;
}