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>
the following query:
Sheep::whereIn('foobar_id', [1, 2, 3, 4, 5])->groupBy('foobar_id')->count();
Also instead of using groupBy I tried distinct('foobar_id') with no effect.
How can I get a result of the counts where doubles of the foobar_id are skipped?
Using Postgres
Sheep::query()
->whereIn('foobar_id', [1, 2, 3, 4, 5])
->distinct()
->count('foobar_id');
I'm having problem with this whereIn. My code looks like this
$arrayKeys = [1, 2, 1, 4, 5, 1];
$products = \App\Product::whereIn('id', $arrayKeys)
->select(['id', 'name', 'outright_price', 'discount', ])
->with('photo', 'colors')
->get();
It only returns the unique id 1, 2, 4, 5. I need to return it 1, 2, 1, 4, 5, 1. How to do it? I don't have any idea.
It's normal, because there is only one row with ID = 1. If for some reason you still want to get a collection with [1, 2, 1, 4, 5, 1] IDs structure, you need to build it manually by using collect() and merge():
$arrayKeys = [1, 2, 1, 4, 5, 1];
$products = collect([]);
$productData = \App\Product::whereIn('id', $arrayKeys)
->select(['id', 'name', 'outright_price', 'discount'])
->with('photo', 'colors')
->get();
foreach ($arrayKeys as $key) {
$products = $products->merge($productData->where('id', $key));
}
dd($products);
I've tested it and it works.
Is there a way to do:
a = b.map{ |e| #return multiple elements to be added to a }
Where rather than returning a single object for each iteration to be added to a, multiple objects can be returned.
I'm currently achieving this with:
a = []
b.map{ |e| a.concat([x,y,z]) }
Is there a way to this in a single line without having to declare a = [] up front?
Use Enumerable#flat_map
b = [0, 3, 6]
a = b.flat_map { |x| [x, x+1, x+2] }
a # => [0, 1, 2, 3, 4, 5, 6, 7, 8]
Use Enumerable#flat_map
Which is probably not much different than:
p [1, 2, 3].map{|num| [1, 2, 3]}.flatten
--output:-
[1, 2, 3, 1, 2, 3, 1, 2, 3]
var.split('/').delete_at(0)
upon inspection returns
""
no matter what the string, however....
var.split('/')
var.delete_at(0)
gives me no trouble. this is probably a stupid question, but are there some sort of restrictions/limitations regarding method chaining like this?
thanks,
brandon
The delete_at method deletes the element but returns the deleted element not the new array.
If you want to always return the object, you can use the tap method (available since Ruby 1.8.7).
a = [1, 2, 3, 4, 5, 6, 7]
a.delete_at(0) # => 1
a # => [2, 3, 4, 5, 6, 7]
a = [1, 2, 3, 4, 5, 6, 7]
a.tap { |a| a.delete_at(0) } # => returns [2, 3, 4, 5, 6, 7]
a # => [2, 3, 4, 5, 6, 7]
literally the first thing I tried to do was:
var.split('/').delete_at(0)
which upon inspection returned
""
no matter what the string
Are you sure? Try the string 'a/b':
irb(main):001:0> var = 'a/b'
=> "a/b"
irb(main):003:0> var.split('/').delete_at(0)
=> "a"
Note that the return value is the element deleted, not the array. The array which you created by performing the split was not stored anywhere and now you have no reference to it. You probably want to do this instead:
a = var.split('/')
a.delete_at(0)
If you always need to delete the first element, you can use other methods that return the object itself, such as slice!, for example:
s = 'foo/bar/baz'
#=> "foo/bar/baz"
s.split('/').slice!(1..-1)
#=> ["bar", "baz"]