Have one array reference two or more - javascript

So I want to know if it's possible to create a scenario like this in javascript:
Lets say I have two arrays:
a = [1, 2, 3]
b = [4, 5, 6]
And then I would like to create an array c that references the two arrays:
c = [1, 2, 3, 4, 5, 6]
However when I change an element in one of the two arrays I would also like to have it automatically affect array c:
a[1] = 1
c = [1, 1, 3, 4, 5, 6]
Is there a way to make this possible in javascript?

The traditional pass-by-reference approach doesn't exist in JavaScript, so you'll have to use a workaround. The simplest way would be to return c dynamically as a combination of a and b using a function. Another would be using an object and getter/setters to manipulate what you get when accessing c.
Example:
var $ = (function () {
/* Hidden arrays */
var a = [1, 2, 3];
var b = [4, 5, 6];
/* Visible object. */
return {
get a () {
return a;
},
set a (v) {
a = v;
},
get b () {
return b;
},
set b (v) {
b = v;
},
get c () {
return a.concat(b);
},
};
})();
/* Example */
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));
$.a = [0, 1, 2];
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));
$.b[1] = 7;
console.log(JSON.stringify($.a), JSON.stringify($.b), JSON.stringify($.c));

You will need a Proxy.
const a = [1,2,3];
const b = [4,5,6];
const c = new Proxy([a,b], {
_localIndex: function(arrays, index) {
index = +index;
if( isNaN(index)) throw new TypeError("Expected numeric index");
if( Math.floor(index) !== index) throw new RangeError("Index must be an integer");
if( index < 0) throw new RangeError("Index must be positive");
for( let i=0; i<arrays.length; i++) {
if( arrays[i].length > index) return [i,index];
index -= arrays[i].length;
}
throw new RangeError("Index out of bounds");
},
get: function(arrays, index) {
if( index === "length") {
return arrays.reduce((a,c)=>a+c.length,0);
}
if( index === "source") {
return arrays;
}
const [arr, idx] = this._localIndex(arrays, index);
return arrays[arr][idx];
},
set: function(arrays, index, value) {
const [arr, idx] = this._localIndex(arrays, index);
arrays[arr][idx] = value;
}
});
console.log("Get c[4]: "+c[4]);
c[2] = 9;
console.log("Updated c[2], a is now: "+JSON.stringify(a));
console.log("Get c's source array: "+JSON.stringify(c.source));
a.push('x');
console.log("Pushed value to a, c is now: "+JSON.stringify(c.source));
MDN docs

Related

How filtered array if this array was exist on data selected? [duplicate]

Let A and B be two sets. I'm looking for really fast or elegant ways to compute the set difference (A - B or A \B, depending on your preference) between them. The two sets are stored and manipulated as Javascript arrays, as the title says.
Notes:
Gecko-specific tricks are okay
I'd prefer sticking to native functions (but I am open to a lightweight library if it's way faster)
I've seen, but not tested, JS.Set (see previous point)
Edit: I noticed a comment about sets containing duplicate elements. When I say "set" I'm referring to the mathematical definition, which means (among other things) that they do not contain duplicate elements.
I don't know if this is most effective, but perhaps the shortest:
var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];
var diff = A.filter(function(x) {
return B.indexOf(x) < 0;
});
console.log(diff); // [2]
Updated to ES6:
const A = [1, 2, 3, 4];
const B = [1, 3, 4, 7];
const diff = A.filter(x => !B.includes(x));
console.log(diff); // [2]
Well, 7 years later, with ES6's Set object it's quite easy (but still not as compact as python's A - B), and reportedly faster than indexOf for large arrays:
console.clear();
let a = new Set([1, 2, 3, 4]);
let b = new Set([5, 4, 3, 2]);
let a_minus_b = new Set([...a].filter(x => !b.has(x)));
let b_minus_a = new Set([...b].filter(x => !a.has(x)));
let a_intersect_b = new Set([...a].filter(x => b.has(x)));
let a_union_b = new Set([...a, ...b]);
console.log(...a_minus_b); // {1}
console.log(...b_minus_a); // {5}
console.log(...a_intersect_b); // {2,3,4}
console.log(...a_union_b); // {1,2,3,4,5}
Looking at a lof of these solutions, they do fine for small cases. But, when you blow them up to a million items, the time complexity starts getting silly.
A.filter(v => B.includes(v))
That starts looking like an O(N^2) solution. Since there is an O(N) solution, let's use it, you can easily modify to not be a generator if you're not up to date on your JS runtime.
function *setMinus(A, B) {
const setA = new Set(A);
const setB = new Set(B);
for (const v of setB.values()) {
if (!setA.delete(v)) {
yield v;
}
}
for (const v of setA.values()) {
yield v;
}
}
a = [1,2,3];
b = [2,3,4];
console.log(Array.from(setMinus(a, b)));
While this is a bit more complex than many of the other solutions, when you have large lists this will be far faster.
Let's take a quick look at the performance difference, running it on a set of 1,000,000 random integers between 0...10,000 we see the following performance results.
setMinus time = 181 ms
diff time = 19099 ms
function buildList(count, range) {
result = [];
for (i = 0; i < count; i++) {
result.push(Math.floor(Math.random() * range))
}
return result;
}
function *setMinus(A, B) {
const setA = new Set(A);
const setB = new Set(B);
for (const v of setB.values()) {
if (!setA.delete(v)) {
yield v;
}
}
for (const v of setA.values()) {
yield v;
}
}
function doDiff(A, B) {
return A.filter(function(x) { return B.indexOf(x) < 0 })
}
const listA = buildList(100_000, 100_000_000);
const listB = buildList(100_000, 100_000_000);
let t0 = process.hrtime.bigint()
const _x = Array.from(setMinus(listA, listB))
let t1 = process.hrtime.bigint()
const _y = doDiff(listA, listB)
let t2 = process.hrtime.bigint()
console.log("setMinus time = ", (t1 - t0) / 1_000_000n, "ms");
console.log("diff time = ", (t2 - t1) / 1_000_000n, "ms");
You can use an object as a map to avoid linearly scanning B for each element of A as in user187291's answer:
function setMinus(A, B) {
var map = {}, C = [];
for(var i = B.length; i--; )
map[B[i].toSource()] = null; // any other value would do
for(var i = A.length; i--; ) {
if(!map.hasOwnProperty(A[i].toSource()))
C.push(A[i]);
}
return C;
}
The non-standard toSource() method is used to get unique property names; if all elements already have unique string representations (as is the case with numbers), you can speed up the code by dropping the toSource() invocations.
If you're using Sets, it can be quite simple and performant:
function setDifference(a, b) {
return new Set(Array.from(a).filter(item => !b.has(item)));
}
Since Sets use Hash functions* under the hood, the has function is much faster than indexOf (this matters if you have, say, more than 100 items).
The shortest, using jQuery, is:
var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];
var diff = $(A).not(B);
console.log(diff.toArray());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I would hash the array B, then keep values from the array A not present in B:
function getHash(array){
// Hash an array into a set of properties
//
// params:
// array - (array) (!nil) the array to hash
//
// return: (object)
// hash object with one property set to true for each value in the array
var hash = {};
for (var i=0; i<array.length; i++){
hash[ array[i] ] = true;
}
return hash;
}
function getDifference(a, b){
// compute the difference a\b
//
// params:
// a - (array) (!nil) first array as a set of values (no duplicates)
// b - (array) (!nil) second array as a set of values (no duplicates)
//
// return: (array)
// the set of values (no duplicates) in array a and not in b,
// listed in the same order as in array a.
var hash = getHash(b);
var diff = [];
for (var i=0; i<a.length; i++){
var value = a[i];
if ( !hash[value]){
diff.push(value);
}
}
return diff;
}
Using Underscore.js (Library for functional JS)
>>> var foo = [1,2,3]
>>> var bar = [1,2,4]
>>> _.difference(foo, bar);
[4]
Some simple functions, borrowing from #milan's answer:
const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
const setIntersection = (a, b) => new Set([...a].filter(x => b.has(x)));
const setUnion = (a, b) => new Set([...a, ...b]);
Usage:
const a = new Set([1, 2]);
const b = new Set([2, 3]);
setDifference(a, b); // Set { 1 }
setIntersection(a, b); // Set { 2 }
setUnion(a, b); // Set { 1, 2, 3 }
Incorporating the idea from Christoph and assuming a couple of non-standard iteration methods on arrays and objects/hashes (each and friends), we can get set difference, union and intersection in linear time in about 20 lines total:
var setOPs = {
minusAB : function (a, b) {
var h = {};
b.each(function (v) { h[v] = true; });
return a.filter(function (v) { return !h.hasOwnProperty(v); });
},
unionAB : function (a, b) {
var h = {}, f = function (v) { h[v] = true; };
a.each(f);
b.each(f);
return myUtils.keys(h);
},
intersectAB : function (a, b) {
var h = {};
a.each(function (v) { h[v] = 1; });
b.each(function (v) { h[v] = (h[v] || 0) + 1; });
var fnSel = function (v, count) { return count > 1; };
var fnVal = function (v, c) { return v; };
return myUtils.select(h, fnSel, fnVal);
}
};
This assumes that each and filter are defined for arrays, and that we have two utility methods:
myUtils.keys(hash): returns an
array with the keys of the hash
myUtils.select(hash, fnSelector,
fnEvaluator): returns an array with
the results of calling fnEvaluator
on the key/value pairs for which
fnSelector returns true.
The select() is loosely inspired by Common Lisp, and is merely filter() and map() rolled into one. (It would be better to have them defined on Object.prototype, but doing so wrecks havoc with jQuery, so I settled for static utility methods.)
Performance: Testing with
var a = [], b = [];
for (var i = 100000; i--; ) {
if (i % 2 !== 0) a.push(i);
if (i % 3 !== 0) b.push(i);
}
gives two sets with 50,000 and 66,666 elements. With these values A-B takes about 75ms, while union and intersection are about 150ms each. (Mac Safari 4.0, using Javascript Date for timing.)
I think that's decent payoff for 20 lines of code.
As for the fasted way, this isn't so elegant but I've run some tests to be sure. Loading one array as an object is far faster to process in large quantities:
var t, a, b, c, objA;
// Fill some arrays to compare
a = Array(30000).fill(0).map(function(v,i) {
return i.toFixed();
});
b = Array(20000).fill(0).map(function(v,i) {
return (i*2).toFixed();
});
// Simple indexOf inside filter
t = Date.now();
c = b.filter(function(v) { return a.indexOf(v) < 0; });
console.log('completed indexOf in %j ms with result %j length', Date.now() - t, c.length);
// Load `a` as Object `A` first to avoid indexOf in filter
t = Date.now();
objA = {};
a.forEach(function(v) { objA[v] = true; });
c = b.filter(function(v) { return !objA[v]; });
console.log('completed Object in %j ms with result %j length', Date.now() - t, c.length);
Results:
completed indexOf in 1219 ms with result 5000 length
completed Object in 8 ms with result 5000 length
However, this works with strings only. If you plan to compare numbered sets you'll want to map results with parseFloat.
The function below are ports of the methods found in Python's set() class and follows the TC39 Set methods proposal.
const
union = (a, b) => new Set([...a, ...b]),
intersection = (a, b) => new Set([...a].filter(x => b.has(x))),
difference = (a, b) => new Set([...a].filter(x => !b.has(x))),
symetricDifference = (a, b) => union(difference(a, b), difference(b, a)),
isSubsetOf = (a, b) => [...b].every(x => a.has(x)),
isSupersetOf = (a, b) => [...a].every(x => b.has(x)),
isDisjointFrom = (a, b) => !intersection(a, b).size;
const
a = new Set([1, 2, 3, 4]),
b = new Set([5, 4, 3, 2]);
console.log(...union(a, b)); // [1, 2, 3, 4, 5]
console.log(...intersection(a, b)); // [2, 3, 4]
console.log(...difference(a, b)); // [1]
console.log(...difference(b, a)); // [5]
console.log(...symetricDifference(a, b)); // [1, 5]
const
c = new Set(['A', 'B', 'C', 'D', 'E']),
d = new Set(['B', 'D']);
console.log(isSubsetOf(c, d)); // true
console.log(isSupersetOf(d, c)); // true
const
e = new Set(['A', 'B', 'C']),
f = new Set(['X', 'Y', 'Z']);
console.log(isDisjointFrom(e, f)); // true
.as-console-wrapper { top: 0; max-height: 100% !important; }
This works, but I think another one is much more shorter, and elegant too
A = [1, 'a', 'b', 12];
B = ['a', 3, 4, 'b'];
diff_set = {
ar : {},
diff : Array(),
remove_set : function(a) { ar = a; return this; },
remove: function (el) {
if(ar.indexOf(el)<0) this.diff.push(el);
}
}
A.forEach(diff_set.remove_set(B).remove,diff_set);
C = diff_set.diff;
Using core-js to polyfill the New Set methods proposal:
import "core-js"
new Set(A).difference(B)
In theory, the time complexity should be Θ(n), where n is the number of elements in B.

Difference of array, result is 1 or undefined

Array a = [1,2,3,4,5,5]
Array b = [1,2,3,5]
c = a - b
which should return c = [4] (5 is repeated twice but I dont want it in the output)
Now here is my code:
function arrayDiff(a, b) {
var array = [];
var diff = [];
for (var i = 0; i<a.length; i++) {
array[a[i]] = true;
}
for (var i = 0; i<b.length; i++) {
if (array[b[i]]) {
delete array[b[i]];
}
else {
array[b[i]] = true;
}
for (var k in array) {
return diff.push(k);
}
}
}
Test.describe("Sample tests", function() {
Test.it("Should pass Sample tests", function() {
Test.assertDeepEquals(arrayDiff([], [4,5]), [], "a was [], b was [4,5]");
Test.assertDeepEquals(arrayDiff([3,4], [3]), [4], "a was [3,4], b was [3]");
Test.assertDeepEquals(arrayDiff([1,8,2], []), [1,8,2], "a was [1,8,2], b was []");
});
});
but it returns weird stuff. Can you please explain why it returns 1 and how do I fix it? This is the console.log output:
a was [], b was [4,5] - Expected: [], instead got: 1
a was [3,4], b was [3] - Expected: [4], instead got: 1
a was [1,8,2], b was [] - Expected: [1, 8, 2], instead got: undefined
Unhandled rejection TestError: a was [], b was [4,5] - Expected: [], instead got: 1
Can you please help me fix it?
there are couple of problems in your code
3rd for loop that is nested inside the 2nd for loop should not be nested inside the 2nd loop.
.push() method adds a new item in the array and returns the length of the array after adding the new item. Instead of returning the return value of push function, you need to return diff array.
Here's the fixed code
const a = [1,2,3,4,5,5];
const b = [1,2,3,5];
function arrayDiff(a, b) {
var array = [];
var diff = [];
for (var i = 0; i<a.length; i++) {
array[a[i]] = true;
}
for (var i = 0; i<b.length; i++) {
if (array[b[i]]) {
delete array[b[i]];
}
else {
array[b[i]] = true;
}
}
for (var k in array) {
diff.push(Number(k));
}
return diff;
}
console.log(arrayDiff(a, b));
Edit
As per your comments, if a = [] and b = [1, 2] then output should be [] and for a = [1, 8, 2] and b = [], output should be [1, 8 ,2].
This isn't possible with your code as you are finding the difference based on array indexes and boolean values.
You can get the desired output by filtering the array a and checking if the current element in array a exists in array b or not.
let a = [1, 8 ,2];
let b = [];
function arrayDiff(a, b) {
return a.filter(n => !b.includes(n));
}
console.log(arrayDiff(a, b));
Your code looks good. It may need some modification to make it simple.
function arrayDiff(a, b) {
return a.filter((aItem) => b.indexOf(aItem) === -1);
}
You could take a Set and filter the array.
function arrayDiff(a, b) {
const setB = new Set(b);
return a.filter(v => !setB.has(v));
}
console.log(arrayDiff([1, 2, 3, 4, 5, 5], [1, 2, 3, 5]));

How to replace array elements in place

I want to attach a new method to the Array.prototype:
Array.prototype.uniq = function(){
return this.filter((val, index) => {
return this.indexOf(val) === index;
});
};
var a = [1, 1, 2, 3];
console.log(a.uniq()); // output: [1,2,3]
console.log(a); // output: [1,1,2,3]
The method removes duplicates from an array. The problem I have is that whenever uniq is called, a new array is returned. I want to do something like this:
Array.prototype.uniq = function(){
this = this.filter((val, index) => { // "ReferenceError: Invalid left-hand side in assignment
return this.indexOf(val) === index;
});
};
so that:
var a = [1, 1, 2, 3];
a.uniq();
console.log(a); // output: [1,2,3]
What should I do?
You can iterate over the array using for loop and use splice if the index are not same.
Array.prototype.uniq = function () {
// Reverse iterate
for (var i = this.length - 1; i >= 0; i--) {
// If duplicate
if (this.indexOf(this[i]) !== i) {
// Remove from array
this.splice(i, 1);
}
}
// Return updated array
return this;
};
var a = [1, 1, 2, 3];
a.uniq();
console.log(a); // output: [1,2,3]

Javascript equivalent to Clojure's "reductions" or python's itertools.accumulate

Is there a JavaScript equivalent to Clojure's "reductions" function or Python's itertools.accumulate? In other words, given an array [x_0, x_1, x_2 ... x_n-1] and a function f(prev, next), it would return an array of length n with values:
[x_0, f(x_0, x_1), f(f(x_0, x_1), x_2)... f(f(f(...)), x_n)]
I'm simulating the desired behavior below:
function accumsum(prev, next) {
last = prev[prev.length - 1] || 0;
prev.push(last + next);
return prev;
}
var x = [1, 1, 1, 1];
var y = x.reduce(accumsum, []);
var z = y.reduce(accumsum, []);
console.log(x);
console.log(y);
console.log(z);
which displays:
[ 1, 1, 1, 1 ]
[ 1, 2, 3, 4 ]
[ 1, 3, 6, 10 ]
But I'm wondering if there is a way to write something simpler like
[1, 1, 1, 1].reductions(function(prev, next) {return prev + next;});
If not, is there a more idiomatic way to do this in JavaScript than what I wrote?
var a = [1, 1, 1, 1];
var c = 0;
a.map(function(x) { return c += x; })
// => [1, 2, 3, 4]
a.reduce(function(c, a) {
c.push(c[c.length - 1] + a);
return c;
}, [0]).slice(1);
// => [1, 2, 3, 4]
I'd use the first one, personally.
EDIT:
Is there a way of doing your first suggestion that doesn't require me to have a random global variable (c in this case) floating around? If I forgot to re-initialize c back to 0, the second time I wrote a.map(...) it would give the wrong answer.
Sure - you can encapsulate it.
function cumulativeReduce(fn, start, array) {
var c = start;
return array.map(function(x) {
return (c = fn(c, x));
});
}
cumulativeReduce(function(c, a) { return c + a; }, 0, [1, 1, 1, 1]);
// => [1, 2, 3, 4]
c
// => ReferenceError - no dangling global variables
I wrote a stateless version
function reductions(coll, reducer, init) {
if (!coll.length) {
return [init]
}
if (init === undefined) {
return reductions(_.drop(coll, 1), reducer, _.first(coll))
}
return [init].concat(reductions(_.drop(coll, 1), reducer, reducer(init, _.first(coll))))
}
For posterity, if you're in a situation where you're using an older version of JavaScript, or don't have access to Underscore.
It's not difficult to implement from scratch and has some educational value.
Here's one way to do it:
function reduce(a, fn, memo) {
var i;
for (i = 0; i < a.length; ++i) {
if ( typeof memo === 'undefined' && i === 0 ) memo = a[i];
else memo = fn(memo, a[i]);
}
return memo;
}
Also, other higher order functions can be written in terms of reduce, e.g. "map", shown here:
function map(a, fn) {
return reduce(a, function(memo, x) {
return memo.concat(fn(a));
}, []);
}
for reference the equivalent imperative (and faster) version of map would be:
function map2(a, fn) {
var newA = [], i;
for (i = 0; i < a.length; ++i) {
newA.push(fn(a[i]));
}
return newA;
}

javascript remove array from array

Assume we have the following arrays:
a = [1, 2, 3, 4, 5]
and
b = [2, 3]
How can I subtract b from a? So that we have c = a - b which should be equal to [1, 4, 5]. jQuery solution would also be fine.
Assuming you're on a browser that has Array.prototype.filter and Array.prototype.indexOf, you could use this:
var c = a.filter(function(item) {
return b.indexOf(item) === -1;
});
If the browser in question does not have those methods, you may be able to shim them.
This is a modified version of the answer posted by #icktoofay.
In ES6 we can make use of:
Array.prototype.contains()
Array.prototype.filter()
Arrow functions
This will simplify our code to:
var c = a.filter(x => !b.includes(x));
Demo:
var a = [1, 2, 3, 4, 5];
var b = [2, 3];
var c = a.filter(x => !b.includes(x));
console.log(c);
For code that would work in all browsers, you would have to manually find each element from b in a and remove it.
var a = [1, 2, 3, 4, 5];
var b = [2, 3];
var result = [], found;
for (var i = 0; i < a.length; i++) {
found = false;
// find a[i] in b
for (var j = 0; j < b.length; j++) {
if (a[i] == b[j]) {
found = true;
break;
}
}
if (!found) {
result.push(a[i]);
}
}
// The array result now contains just the items from a that are not in b
Working example here: http://jsfiddle.net/jfriend00/xkBzR/
And, here's a version that could be faster for large arrays because it puts everything into an object for hashed lookups rather than brute force array searching:
var a = [1, 2, 3, 4, 5];
var b = [2, 3];
function filterArray(src, filt) {
var temp = {}, i, result = [];
// load contents of filt into object keys for faster lookup
for (i = 0; i < filt.length; i++) {
temp[filt[i]] = true;
}
// go through src
for (i = 0; i < src.length; i++) {
if (!(src[i] in temp)) {
result.push(src[i]);
}
}
return(result);
}
var filtered = filterArray(a, b);
Working example here: http://jsfiddle.net/jfriend00/LUcx6/
For the ones struggling with Objects, like Date, you'll find out that two different objects are never equal to each other, even if they have the same values, so the answers above wouldn't work.
Here is an answer to this problem in ES6.
const c = a.filter(aObject => b.findIndex(bObject => aObject.valueOf() === bObject.valueOf()) === -1)
Here an implementation for try works in all browsers:
if('filter' in Array == false) {
Array.prototype.filter =
function(callback) {
if(null == this || void 0 == this) {
return;
}
var filtered = [];
for(i = 0, len = this.length; i < len; i++) {
var tmp = this[i];
if(callback(tmp)) {
filtered.push(tmp);
}
}
return filtered;
}
}
a = [1, 2, 3, 4, 5];
b = [2, 3];
var c = a.filter(function(item) { /*implementation of icktoofay */
return b.indexOf(item) === -1;
});
Might be an outdated query but i thought this might be useful to someone.
let first = [1,2,3,4,5,6,7,9];
let second = [2,4,6,8];
const difference = first.filter(item=>!second.includes(item));
console.log(difference);//[ 1, 3, 6,7]
/*
the above will not work for objects with properties
This might do the trick
*/
const firstObj = [{a:1,b:2},{a:3,b:4},{a:5,b:6},{a:7,b:8}]//not ideal. I know
const secondObj = [{a:3,b:4},{a:7,b:8}]
const objDiff = firstObj.filter(obj=>
!secondObj.find(sec=>//take note of the "!"
sec.a===obj.a
&&//or use || if you want to check for either or
sec.b===obj.b
)//this is formatted so that it is easily readable
);
console.log(objDiff)/*
[
{
"a": 1,
"b": 2
},
{
"a": 5,
"b": 6
}
]
*/

Categories

Resources