Related
I tried to de-duplicate the sent arrays and then merge them into arrays
import { from, BehaviorSubject, distinct, switchMap, toArray } from "rxjs";
let list$ = new BehaviorSubject([1, 2, 3, 2, 3, 5]);
list$.pipe(
switchMap((e) => from(e)),
distinct(),
toArray()
).subscribe(console.log);
expected result:
BehaviorSubject -> [1, 2, 3, 2, 3, 5]
switchMap -> 1 2 3 2 3 5
distinct -> 1 2 3 5
toArray -> [1, 2, 3, 5]
console.log -> [1, 2, 3, 5]
Actually did not receive any value in console.log, why is this and how can I work as expected
"rxjs": "^7.2.0"
toArray only emits once the source observable completes.
The following should work as expected.
list$.pipe(
take(1),
switchMap(e => e),
distinct(),
toArray()
).subscribe(console.log);
If what you really want to do is filter unique values of an array, then RxJS's unique operator might be overkill. I wouldn't bother turning your array into a stream. Just filter the array.
list$.pipe(
map(a => [...new Set(a)])
).subscribe(console.log);
So, if the source does not stop after the first notification, I assume that it will continue emit other arrays and that you want to filter the duplicates on each array emitted. In other words, if the list$ of your example emits first [1, 2, 3, 2, 3, 5] and then [3, 2, 1, 6, 6, 6,] what you want to log are 2 arrays, [1, 2, 3, 5] and [3, 2, 1, 6].
If my assumption is right, than the solution could be the following
list$.pipe(
concatMap((e) => from(e).pipe(
distinct(),
toArray()
)),
).subscribe(console.log);
The trick here is that each from(e) stream will complete when there are no more elements in the array. Therefore, since it completes, the toArray operator can actually work.
scan could do the trick.
list$.pipe(
switchMap((e) => from(e)),
distinct(),
scan((acc, curr) => [...acc, curr], []),
).subscribe(console.log);
// will print: [1], [1, 2], [1, 2, 3], [1, 2, 3, 5]
You could insert debounceTime in the pipe, if you need less emissions:
list$.pipe(
switchMap((e) => from(e)),
distinct(),
scan((acc, curr) => [...acc, curr], []),
debounceTime(0)
).subscribe(console.log); // will print [1, 2, 3, 5]
If the only requirement is to remove duplicates, you're better off handling it using vaniall JS. See here: https://stackoverflow.com/a/9229821/6513921
We'll take the shortest solution without any regards to performance: uniq = [...new Set(array)];
You could then write a custom RxJS operator to include it in the pipe with other operators.
const { BehaviorSubject, from } = rxjs;
const { map, switchMap } = rxjs.operators;
const uniqueArray = (obs$) => {
return (obs$) => {
return obs$.pipe(
map(arr => [...new Set(arr)])
);
};
};
const sub = new BehaviorSubject([1, 2, 3, 2, 3, 5]);
sub.asObservable().pipe(
uniqueArray()
).subscribe(console.log);
sub.next([6, 3, 1, 6, 7, 1, 1]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>
I'm weirdly stuck with the following:
I have an Observable that emits either an array with some items or an empty array, and this Observable emits only once - let's call it startingArray$
Then I have a hot Observable that emits individual items that I would like to push to startingArray once startingArray has been emitted - let's call it additions$
What code gives me the resulting observable startingArrayPlusAdditions$ that continuosly grows as more items are emitted?
startingArrayPlusAdditions$ should emit every time additions$ emits, but it should not emit when startingArray$ emits initially
Not sure that I understood all your problem but here's a proposal:
const { Observable } = Rx;
const startingArray$ = Observable.of([1, 2, 3]);
const additions$ = Observable.from([4, 5, 6, 7, 8]);
const startingArrayPlusAdditions$ = startingArray$
.combineLatest(additions$)
.scan((acc, current) => {
const [startingArray, addition] = current;
if (acc === null) {
return [...startingArray, addition];
} else {
acc.push(addition);
return acc;
}
}, null)
.do(console.log)
.subscribe();
The output is:
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
So just as you asked:
startingArrayPlusAdditions$ should emit every time additions$ emits, but it should not emit when startingArray$ emits initially
Here's a working Plunkr: https://plnkr.co/edit/rKXLJrmA7mSzpQgoemlD?p=preview
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
def mergeArrays (ar1, ar2)
result = (ar1 << ar2).flatten!
require 'pp'
pp %w(result)
end
As simple as this. I am trying to merge these two arrays and display the result. I am also brand-brand new to Ruby. This is the first function I am writing in this language. Trying to learn here. Also how can I remove the duplicates?
It would help if you give example inputs and outputs so we know exactly what you want. When you use the word "merge", I think you actually just want to add the arrays together:
ar1 = [1, 2, 3]
ar2 = [3, 4, 5]
ar3 = ar1 + ar2 # => [1, 2, 3, 3, 4, 5]
Now if you want to remove duplicates, use Array#uniq:
ar4 = ar3.uniq # => [1, 2, 3, 4, 5]
There is no need to write a method to do any of this since the Ruby Array class already supports it. You should skim through the documentation of the Array class to learn more things you can do with arrays.
What do you mean 'not working'?
Similar questions have been asked here:
Array Merge (Union)
You have two options: the pipe operator (a1 | a2) or concatenate-and-uniq ((a1 + a2).uniq).
Also be careful about using <<, this will modify the original variable, concatenating ar2 onto the end of the original ar1.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
result = (nums1<< nums2).flatten!
nums1
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
nums2
=> [5, 6, 7, 8, 9]
result
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
Additionally- just another Ruby tip, you do not need the destructive flatten! with ! versus the regular flatten. The regular flatten method will return a new Array, which you assign to result in your case. flatten! will flatten self in place, altering whatever Array it's called upon, rather than returning a new array.
You can merge Arrays using '+' operator and you can ignore the duplicated values using .uniq
>> nums1 = Array[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> nums2 = Array[5, 6, 7, 8, 9]
=> [5, 6, 7, 8, 9]
>> def mergeArrays (nums1, nums2)
>> result = (nums1 + nums2).uniq
>> end
=> :mergeArrays
>> mergeArrays(nums1,nums2)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
p nums1.concat(nums2).uniq
I have a class that contains an array. This is an example class. a is an array
class Holder
attr_accessor :a
end
I am trying to make a copy of an object and execute a function on its array. An example situation:
t = Holder.new
t.a = (1..9).to_a
t2= Holder.new
t2.a = t.a
t2.a[2]+=10
t2.a
# => [1, 2, 13, 4, 5, 6, 7, 8, 9]
t.a
# => [1, 2, 13, 4, 5, 6, 7, 8, 9]
Both array in each object are effected. I don't know how to make them separate. I tried with clone and dup too.
dupt = t2.dup
dupt.a[8]+=10
dupt
# => #<Holder:0x007fb6e193b0a8 #a=[1, 2, 13, 4, 5, 6, 7, 8, 19]>
t2
# => #<Holder:0x007fb6e1962ba8 #a=[1, 2, 13, 4, 5, 6, 7, 8, 19]>
You need to call dup on the Array, not on your Holder object. dup will not create copies of all the sub-elements in the object you are trying to copy.
t2.a = t.a.dup
I got bunch of 2s and 3s that i have to multiply together. Now i want to generate every unique combination of these numbers such that when these combination are multiplied does not exceed 10.
For example, I have something like this.
2*2*2*2*3*3*3
I can have following valid combination from above.
4*4*9*3
8*6*9
4*2*6*9
But the following combination are wrong. 16*3*9 and 4*4*27.
Can somebody suggest an algorithm to do this?
The solution can be built up recursively. Consider the input as a list of numbers such as [2,2,2,2,3,3,3]. Divide the list into a prefix (such as [2,2]) and the corresponding suffix ([2,2,3,3,3] in this case). Now multiple the entries in the prefix (and we get 4 in this example), and recursively solve the same problem for the suffix. Inserting the value from the multiplicity to the beginning of each of the solution for the suffix, we get the answer for the original problem.
In the following Python code, the recursive logic is defined in the function collapse, which finds all valid prefix (whose multiplicity is less than 10) and insert the multiplicity to all the results returned in collapsing the remaining data after cutting out the prefix (collapse(d[prefix_len:])).
a = [2,2,2,2,3,3,3]
def collapse(d):
if len(d) > 0:
for prefix_len in range(1, len(d) + 1):
prefix = reduce(lambda x,y: x*y, d[:prefix_len], 1)
if prefix > 10:
break
for suffix in collapse(d[prefix_len:]):
yield [prefix] + suffix
else:
yield d
for i in collapse(a):
print i
Output is
[2, 2, 2, 2, 3, 3, 3]
[2, 2, 2, 2, 3, 9]
[2, 2, 2, 2, 9, 3]
[2, 2, 2, 6, 3, 3]
[2, 2, 2, 6, 9]
[2, 2, 4, 3, 3, 3]
[2, 2, 4, 3, 9]
[2, 2, 4, 9, 3]
[2, 4, 2, 3, 3, 3]
[2, 4, 2, 3, 9]
[2, 4, 2, 9, 3]
[2, 4, 6, 3, 3]
[2, 4, 6, 9]
[2, 8, 3, 3, 3]
[2, 8, 3, 9]
[2, 8, 9, 3]
[4, 2, 2, 3, 3, 3]
[4, 2, 2, 3, 9]
[4, 2, 2, 9, 3]
[4, 2, 6, 3, 3]
[4, 2, 6, 9]
[4, 4, 3, 3, 3]
[4, 4, 3, 9]
[4, 4, 9, 3]
[8, 2, 3, 3, 3]
[8, 2, 3, 9]
[8, 2, 9, 3]
[8, 6, 3, 3]
[8, 6, 9]
If order matters (i.e. 2*4*2 is not the same as 2*2*4) and you have to list them (i.e. "generate") then you should just do it all recursively. In Scala:
def combine(who: List[Int], limit: Int=10): List[List[Int]] = who match {
case x :: y :: more =>
combine(y :: more, limit).map(x :: _) :::
(if (x*y<limit) combine((x*y) :: more, limit) else Nil)
case x :: Nil => List(who)
case Nil => List()
}
You may not know Scala, so here's how the three cases work. First case: list has at least two elements remaining. Pick off the first element and add it to all possible later combinations. Then, if you can merge the first and second elements, do so, and find all combinations of the list that starts with that. Second case: trivial list with only one element; return that as the only thing in the list. Third case: degenerate input (no values given); return an empty list.
(In Scala, ::: concatenates two lists together, while x :: list sticks x on the front of list. When you're matching, it goes the other way around: case x :: stuff is used if the list can be broken into an element x and the rest, stuff. Nil is the empty list.)
Here it is in action:
scala> combine( List(2,2,2,2,3,3,3) )
res18: List[List[Int]] = List(List(2, 2, 2, 2, 3, 3, 3), List(2, 2, 2, 2, 3, 9),
List(2, 2, 2, 2, 9, 3), List(2, 2, 2, 6, 3, 3), List(2, 2, 2, 6, 9),
List(2, 2, 4, 3, 3, 3), List(2, 2, 4, 3, 9), List(2, 2, 4, 9, 3),
List(2, 4, 2, 3, 3, 3), List(2, 4, 2, 3, 9), List(2, 4, 2, 9, 3), List(2, 4, 6, 3, 3),
List(2, 4, 6, 9), List(2, 8, 3, 3, 3), List(2, 8, 3, 9), List(2, 8, 9, 3),
List(4, 2, 2, 3, 3, 3), List(4, 2, 2, 3, 9), List(4, 2, 2, 9, 3), List(4, 2, 6, 3, 3),
List(4, 2, 6, 9), List(4, 4, 3, 3, 3), List(4, 4, 3, 9), List(4, 4, 9, 3),
List(8, 2, 3, 3, 3), List(8, 2, 3, 9), List(8, 2, 9, 3), List(8, 6, 3, 3), List(8, 6, 9))
Edit: if you just wanted to count them, you'd use a different type of recurrence. Let S(n) be the number of combinations taken from the nth onwards, and let L(n) be the value of the nth item in your list. Then
S(i) = S(i+1) +
if (L(i)+L(i+1)<10) S(i+2) +
if (L(i)+...+L(i+2)<10) S(i+3) +
....
So you start with the last item--only one possibility there--and work your way backwards in order using this formula. (If this is what you're after, I'll write code that does it, but hopefully the algorithm is clear enough as is.)