RxJs - get duplicate items of two observables - javascript

I need to get duplicate items of two streams. I think I almost managed to do it, but only if those items that are duplicate of second stream, goes in order. For ex:
This works:
first = Observable.of(1, 2, 3)
second = Observable.of(2, 3, 1)
But this doesn't:
first = Observable.of(1, 4, 3)
second = Observable.of(1, 2, 3)
When my loop gets to the 4, it breaks:
EmptyError {name: "EmptyError", stack: "EmptyError: no elements in
sequence↵ at new Emp…e
(http://localhost:4200/vendor.bundle.js:161:22)", message: "no
elements in sequence"}
Whole my code is in one function, you can copy/paste and test it:
findDublicates() {
let match = 0; // setting it to 0, so later could assign other number
let keys = []; // list of maching keys
let elementAt = 0; // index of item of first observable
let allKeys$;
let validKeys$;
// counting the length of both observables, so this will be the number of loops
// that checks for dublicates
let allKeysLength;
let validKeysLength;
let allKeysLength$ = Observable.of(2, 1, 4, 5, 7).count()
allKeysLength$.subscribe(val => allKeysLength = val)
let validKeysLength$ = Observable.of(1, 2, 3, 8, 5).count()
validKeysLength$.subscribe(val => validKeysLength = val)
let cycles = Math.min(allKeysLength,validKeysLength); // length of the shorter observable
// wrapping it in a function so when called variables will take new values
function defineObs() {
allKeys$ = Observable.of(2, 1, 4, 5, 7)
.elementAt(elementAt).take(1);
validKeys$ = Observable.of(1, 2, 3, 8, 5)
.filter((x) => (x === match)).first();
}
for (var i=0; i<=cycles; i++) {
defineObs();
allKeys$.subscribe(
function (val) { match = val },
function (err) { console.log(err) },
function () { console.log('Done filter')}
);
validKeys$.subscribe(
function (val) { keys.push(val) },
function (err) { console.log(err) },
function () { console.log('Done push')}
);
elementAt += 1;
cycles -= 1;
}
return console.log(keys);
}
Thanks for any help.

If you don't care about which stream emits the first value of a set of duplicates, you may just merge them and treat as finding duplicate values on a single stream:
first.merge(second)
.scan(([ dupes, uniques ], next) =>
[ uniques.has(next) ? dupes.add(next) : dupes, uniques.add(next) ],
[ new Set(), new Set() ]
)
.map(([ dupes ]) => dupes)
Note: the Sets above are immutable, to avoid undefined behavior in scan.

I would check up on Observable.combineLatest and the scan method on an observable sequence.
Here’s what I’m thinking, combine the two observables using combineLatest and apply the scan operator on that. You can even use a Set to ensure uniqueness or even a map and filter.

Related

How to check if an element is present in all of the subarrays passed into the function? JS

function intersection(...bigArr)
{
let results = [];
let compiledArr = [];
for(let i = 0; i < bigArr.length; i++) {//bigArr.length is the number of subarrays
compiledArr.push(...bigArr[i]);//takes each subarray, deconstructs it and the elements are pushed into compiledArr.
}
const frequencyObj = {};
let endBound = bigArr[0].length;
let k = 1
for(let i = 0; i < compiledArr.length; i++)
{
//if the element exists, increase its frequency by 1. If it doesn't, create it and initialize it to 1. After, check if its frequency value === bigArr.length. If so, push to results
let currentElement = compiledArr[i];
if(i === endBound)//the program has reached the next subarray
{
endBound += bigArr[k].length;
k++;
turnTrue(frequencyObj) //turn the boolean value for each property to true. This is because we are analyzing a different subarray.
}
if(!frequencyObj[compiledArr[i]])//if the element DNE in the object, then add it
frequencyObj[compiledArr[i]] = [1, false];
else if( frequencyObj[currentElement] && frequencyObj[currentElement][1] === true)//if the element already exists, we need to make sure that we only increment its frequency iff we are in a different subarray within compiledArr
{
frequencyObj[currentElement][0] += 1;
frequencyObj[currentElement][1] = false;
//check if the frequency of that element === bigArr.length, it means it appears in all subarrays.
if(frequencyObj[currentElement][0] === bigArr.length)
results.push(currentElement);
}
}
return results;
}
function turnTrue(obj)
{
for(let key in obj)
obj[key][1] = true;
}
let result = intersection([1,2,1], [4,1,3,1,4], [3, 1, 2,6]);
console.log(result);
The program above has the the purpose of outputting an array with elements that are present in all of the subarrays passed into the intersection function.
This program accounts for duplicates within one subarray. The frequency is only marked when an element appears for the first time in a subarray. For example, given the three test subarrays [1,2,1], [4,1,3,1,4], [3, 1, 2,6], any element can only have a max frequency of the number of subarrays(in this case 3). An element is only marked once per subarray.
This is the only solution I could think of and I know that there exists a more simpler solution. Can anyone rate this code and indicate what could be a better solution?
It sounds like you're asking for a basic Set intersection, which can be done using the standard JavaScript Set object. Please see Set for more info.
You could however be asking for a count of how many times each element appears, but your question isn't explicit about this (and can only be either 0 if the set is empty, or the .length of your data array)
data = [ [1,2,1], [4,1,3,1,4], [3, 1, 2,6] ]
// intersection is taken verbatim from MDN docs linked
function intersection(setA, setB) {
const _intersection = new Set();
for (const elem of setB) {
if (setA.has(elem)) {
_intersection.add(elem);
}
}
return _intersection;
}
let set = data.reduce( (a, e) => {
// test includes the possibility that the leading elements are empty
return (a && a.size)? intersection(a, new Set(e)) : new Set(e)
}, null)
console.log([...set])

Combine map() and concat() JavaScript - cleaner code question

Is it possible to combine map() and concat() in the following code to make it cleaner/shorter?
const firstColumnData = data.map((item: any) => {
return item.firstColumn;
});
const secondColumnData = data.map((item: any) => {
return item.secondColumn;
});
const allData = firstColumnData.concat(secondColumnData);
For context, later in the file allData is mapped through to populate data into columns. The specific data depends on which page is calling the component.
Basically, I am wondering if I can skip the declaration of firstColumnData and secondColumn data and assign the value to allData directly. This is an example of how I tried to refactor, but it did not work. (white page, could not render)
const allData = data.map((item: any => {
return item.firstColumn.concat(item.secondColumn)
});
You can use a single reduce() operation. Arguably, what you save by only having to iterate once, you lose in readability:
const data =[
{firstColumn: 1, secondColumn: 2},
{firstColumn: 3, secondColumn: 4}
];
const result = data.reduce((a, {firstColumn, secondColumn}, i, {length}) => {
a[i] = firstColumn;
a[i + length] = secondColumn;
return a;
}, []);
console.log(result);
I agree with Robby that readability is probably the most important part in this.
You could one line this though, as:
const allData = [...data.map(item => item.firstColumn), ...data.map(item.secondColumn)];
but in this case you're still looping twice, so you haven't saved any computation, you've just made it shorter to write.
Looping your current logic
My original answer below is, of course, performant as the proverbial January molasses even though I like the shape of it.
A little testing shows that just putting your current logic into a loop offers about the same or better performance than a generalized version RobbyCornelissen's answer (unless you unroll the loop in the reduce...) and has the benefit of simplicity. It relies on defining an array of column properties to iterate over.
const
data = [{ firstColumn: 1, secondColumn: 2 }, { firstColumn: 3, secondColumn: 4 }],
columns = ['firstColumn', 'secondColumn'],
result = [].concat(...columns.map(col => data.map((item) => item[col])));
console.log(result);
Generalized reduce()
const
data = [{ firstColumn: 1, secondColumn: 2 }, { firstColumn: 3, secondColumn: 4 }],
columns = ['firstColumn', 'secondColumn'],
result = data.reduce((a, item, i, { length }) => {
for (let j = 0; j < columns.length; j++) {
a[i + length * j] = item[columns[j]]
}
return a
}, []);
console.log(result);
Zip (original answer)
If the properties are guaranteed to be in order you could 'zip' the Object.values. This will handle any number of properties without explicit desctructuring.
const data = [
{ firstColumn: 1, secondColumn: 2 },
{ firstColumn: 3, secondColumn: 4 }
];
const result = zip(...data.map(Object.values)).flat()
console.log(result)
<script>
/**
* #see https://stackoverflow.com/a/10284006/13762301
*/
const zip = (...rows) => [...rows[0]].map((_, c) => rows.map((row) => row[c]));
</script>
But to avoid relying on property order you can still destructure.
const result = zip(...data.map(({ firstColumn, secondColumn }) => [firstColumn, secondColumn])).flat()
see: Javascript equivalent of Python's zip function for more discussion on 'zip'.

Compare two arrays and create a new array with the differences, accounting for duplicates

Lets say I have two arrays containing numbers..
[1,1,7,6],
[1,7]
I need to create a new array out of the numbers that do not appear in the second array, but also account for there being two 1s.
I have tried using lodash's _.difference([1,1,7,6],[1,7])
as well as some other plain Javascript functions, but they do not account for the there being two 1s, so I just end up with [6]
My desired outcome would be [1,6]
Order does not matter
Iterate over the first array. For each element in the first array, search the second. If it exists, insert null into that index. If it doesn't exist, then push it to a new array with your differences
const difference = (arr1, arr2) => {
const differenceArr = []
for (let i = 0; i < arr1.length; i++) {
const matchingIdx = arr2.indexOf(arr1[i]);
if (matchingIdx !== -1) {
arr2[matchingIdx] = null
} else {
differenceArr.push(arr1[i])
}
}
return differenceArr
}
console.log(difference([1, 1, 7, 7, 6],[1, 1, 7]))
A shorter script:
function differenceWithDuplicates(a, b) {
while(b.length) a.splice(a.indexOf(b[0]), +a.includes(b.shift()));
return a;
}
console.log(differenceWithDuplicates([1, 1, 7, 6],[1, 7]));
Or just make the while, without call it inside a function.
You could do something along the lines of:
const diff = (array1, array2) => {
const _array2 = Array.from(array2);
const difference = [];
array1.forEach((array1Item) => {
const matchedIndex = _array2.findIndex(array2Item => array2Item === array1Item);
if (matchedIndex > -1) {
// Remove from second array
_array2.splice(matchedIndex, 1);
} else {
// No match found, add to difference array
difference.push(array1Item);
}
});
// Return the remaining items in _array2 and difference
return [ ..._array2, ...difference ];
}
By copying the second array, you can remove any matches from it. And by combining that with matching array1 > array2, you get differences in both directions, including duplicates.
Working fiddle: https://jsfiddle.net/3v34tjnq/

Changing one item in a list In immutable.js

I'm using immutable.js, my data structure is like:
class ItemList extends Record({
items: new List()
})
I want to write function that change one item in this list and keep other the same. For example, a list of {1, 2, 3, 4}, I need a function if an item equals 2, change it to 5.
I'm using something like
updateInfo(updatedInfo) {
return this.withMutations(itemList => {
itemList.set('items', list);
});
}
My question is in this function, how can I just update one item? where should I put the if judgment?
Thanks!
NB: As mentioned by another answer, there is also the undocumented indexOf method which may be easier to use in some cases, taking only the value to find as parameter.
Using findIndex to find the index of the value you need to change and set with the index to change:
list = Immutable.List.of(1, 2, 3, 4);
list = list.set(list.findIndex(function(item) {
return item === 2;
}), 5);
ES6:
list = list.set(list.findIndex((item) => item === 2), 5);
If you need the old value to change it, you can use update instead of set like:
list = list.update(list.findIndex(function(item) {
return item === 2;
}), function(oldValue) {
return 5;
});
ES6:
list = list.update(list.findIndex((item) => item === 2), (oldValue) => 5);
It is easy.
list = Immutable.List.of(1, 2, 3, 4);
list = list.set(list.indexOf(2), 5);
console.log(list.get(1)); //5
A much cleaner version, based on forEach. Its a sideeffect (mutates an otherwise immutable list), so the syntax is similar to using a mutable list -
var list = Immutable.List.of(1, 2, 3, 4);
// Notice no LHS assignment is required as
// forEach is a side-effect method.
list.forEach((value, index, theList) => {
// You can check either value, or index
if (index === soAndSo
|| value.something === something){
// Just change the value!
value.prop = someNewValue;
// Or, in the above case where value
// is not a reference
theList.set(index) = newValue;
// As we found and changed the value
// of interest, lets exit forEach
return false;
}
});
And yes, there's a version for Map too.

Pairwise combinations of entries in a javascript array

I'm given an array of entries in javascript, such as :
var entries = ["cat", "dog", "chicken", "pig"];
I'd now like to iterate over all unique pairwise combinations of them. In this example, I'd like to see:
("cat", "dog"),
("cat", "chicken"),
...
In other languages, like scala, this is super easy. You just do
entries.combinations(2)
is there a similar method or function in a library for javascript? Or do I just have to write it myself the ugly way with nested loops?
var arr = ["cat","dog","chicken","pig"].map(function(item,i,arr) {
return arr.map(function(_item) { if( item != _item) return [item, _item];});
});
This will return the expected results. There are caveats, it does not work in older browsers without shims.
Also the duplicate value is 'undefined' instead of there being 4 arrays of 3. I'm sure there is a more graceful way to handle this.
Array.prototype.map() - MDN
edit
this will give you the proper pairwise combinations.
var arr = ["cat","dog","chicken","pig"].map(function(item,i,arr) {
var tmp = arr.map(function(_item) { if( item != _item) return [item, _item];});
return tmp.splice(tmp.indexOf(undefined),1), tmp;
});
Array splice method - MDN
and here is a more readable version of the same code.
var myArray = ["cat", "dog", "chicken", "pig"];
var pairwise = myArray.map(function(item, index, originalArray) {
var tmp = originalArray.map(function(_item) {
if (item != _item) {
return [item, _item];
}
});
tmp.splice(tmp.indexOf(undefined), 1); // because there is now one undefined index we must remove it.
return tmp;
});
Not as far as I know. I think you have to stick to nested loops.
A similar question has been asked here: Output each combination of an array of numbers with javascript maybe you can find an answer there.
With ES6 syntax, one can use a shorter version of #rlemon's answer:
["cat","dog","chicken","pig"].sort().reduce(
(acc, item, i, arr) => acc.concat(
arr.slice(i + 1).map(_item => [item, _item])
),
[])
This takes care of undefineds, and also outputs only unique combinations, as per OP's question.
After reviewing the question, this answer doesn't correctly solve the question. The question asks for all combinations, but the function below combines all adjacent even and odd indexes of the array.
Here is a pairwise implementation I did using reduce
function pairwise(arr) {
return arr.reduce(function(acc, current, index) {
var isFirstPair = (index % 2) === 0;
if (isFirstPair) {
acc.push([current]);
} else {
lastElement = acc[acc.length - 1];
lastElement.push(current);
}
return acc;
}, []);
};
var nums = [1,2,3,4,5,6];
var res = pairwise(nums);
res.forEach(function(elem) {
console.log(elem);
});
Returns:
[
[1, 2]
[3, 4]
[5, 6]
]
Here's a generic TypeScript implementation (you can get the pure JS by removing the types):
// Returns an array of size
const sizedArray = (n: number): null[] => Array(n).fill(null);
// calls the callback n times
const times = (n: number, cb: () => void): void => {
while (0 < n--) {
cb();
}
};
// Fills up the array with the return values of subsequent calls of cb
const fillWithCb = <T>(n: number, cb: () => T): T[] => sizedArray(n).map(cb);
// Generic to produce pairwise, 3 element wise etc..
const nWise = (n: number): (<T>(array: T[]) => T[][]) => <T>(
array: T[]
): T[][] => {
const iterators = fillWithCb(n, () => array[Symbol.iterator]());
iterators.forEach((it, index) => times(index, () => it.next()));
return fillWithCb(array.length - n + 1, () =>
iterators.map(it => it.next().value)
);
};
// curried nWise with 2 -> pairWise
export const pairWise = nWise(2);
The most effective and simple solution can be reduced and slice. However, if you just want to get values. You can use a generator.
// Util class
function pairWise(arr) {
return {
[Symbol.iterator]: function *() {
for(let i =0; i< arr.length; i= i+2)
yield arr.slice(i, i+2)
}
}
}
// How to use it
for(ent of pairWise([1,2,3,4,5,6, 7])){
console.log(ent)
}
// Output
/*
[ 1, 2 ]
[ 3, 4 ]
[ 5, 6 ]
[ 7 ]
*/

Categories

Resources