Related
I have two streams where each stream has a different set of values and a different amount:
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
merge(flowA, flowB).collect{
Log.i(TAG, it.toString())
}
}
Is it possible to use Kotlin's Flow to merge these two streams so that the result is sorted? So the collected values should end up being:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
The values in each stream is already sorted. I just need to merge them. One very important thing however. I don't want to sort AFTER all the values have been collected. The sorting must be done as each value is emitted. My sample above is over simplified. In the real app, the source for each flow contains large arrays for each item. Waiting for all the values to be collected and then sorting is unacceptable as this would require a large amount of memory. But the basic concept for simple integer values should work for more complex data types as well.
Maybe the filter operator is what I need but that isn't clear as I have little experience with flows.
Disclaimer: This is the first time I've used Flow.
Even though the streams are "already sorted", it seems you cannot control the timing the elements will arrive from the two streams. So, you will only be able to get an ordered list by collecting all the elements, then sorting them.
This worked for me:
val sortedResults = flowA
.onCompletion { emitAll(flowB) }
.toCollection(mutableListOf())
.sorted()
println(sortedResults)
Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
You could use the combine operator to get value from two or more flows and then a flatMapLatest operator like this:
val desiredFlow = combine(getFlowA(),getFlowB()) { a, b ->
val arr = IntArray(a.size + b.size)
var i = 0
var j = 0
var k = 0
while (i < a.size && j < b.size)
arr[k++] = if (a[i] < b[j]) a[i++] else b[j++]
while (i < a.size)
arr[k++] = a[i++]
while (j < b.size)
arr[k++] = b[j++]
arr
}.flatMapLatest { result ->
flow {
emit(result.toMutableList())
}
}
fun getFlowA(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf<Int>(0,4,9))
}
}
fun getFlowB(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf(1,2,3,4,5,6,7,8))
}
}
I'm from the Android dev world and not expert with Flows so kindly pardon me if isn't what you expected, but this produces the final output as:
[0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9]
After going through the question, I have two ideas either using a flatten merge or using a delay.
The first idea looks something like this.
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
val newList = mutableListOf<Int>()
val a = flowOf(flowA, flowB).flattenMerge().collect { value ->
when {
newList.isEmpty() -> newList.add(value)
newList.last() <= value -> newList.add(value)
newList.last() > value -> {
//sorting values as they arrive
val i = newList.lastIndex
newList.add(value)
val newValue = newList[i]
newList[i] = newList[i + 1]
newList[i + 1] = newValue
}
}
}
Log.i(TAG, newList.toString())
}
In the second one, add appropriate delays to your first 2 flows.
PS:-
Android Studio gives a warning while using flattenMerge.
This declaration is in a preview state and can be changed in a backwards-incompatible manner with a best-effort migration. Its usage should be marked with '#kotlinx.coroutines.FlowPreview' or '#OptIn(kotlinx.coroutines.FlowPreview::class)' if you accept the drawback of relying on preview API
I don't think you can do this using the built-in flow operators, but you should certainly be able to implement your own. You can use channelFlow for that purpose. This is a versatile way to build a flow that gives us a coroutine scope to work in, and lets us emit items by sending to a channel.
fun <T> mergeOrdered(flowA: Flow<T>, flowB: Flow<T>) = channelFlow {
val channelA = flowA.produceIn(this)
val channelB = flowB.produceIn(this)
var a = channelA.receive()
var b = channelB.receive()
while (isActive) {
if (a < b) {
send(a)
a = channelA.receive()
} else {
send(b)
b = channelB.receive()
}
}
}
This simple example doesn't handle what happens when flowA and flowB run out of elements, but that should be easy enough to add.
Assuming i have an parent class that I filter on various properties, one of which is a property that is an array of items .
Now say that i want to only return the parent item if my array of items as above a min value and below a max value ...that's fine i can work that bit out;
What if i then want to then sort on the filtered result set of those items
I made a c# fiddle example to show what im trying to achieve :
https://dotnetfiddle.net/mV4d28
(note that foo2 is returned first even though foo1 has items in its array that are less that those in foo2)
As i need to do this using a index i need the index to be able to compute the order by based on the filter criteria used in my query.
I know elasticsearch has an inner hits function that dose this and mongo has pipelines which also dose this so im sure Raven must have a way of doing this too ?
I was hoping using just index and a transform with prams i could achieve this so I tried it:
my index and transform look like this
public class familyTransfrom : AbstractTransformerCreationTask<ParentItem>
{
public class Result : ParentItem{
public double[] ChildItemValuesFiltered { get; set; }
}
public familyTransfrom(){
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
}
}
public class familyIndex : AbstractIndexCreationTask<ParentItem>{
public class Result : ParentItem {
public double[] ChildItemValues { get; set; }
}
public familyIndex(){
Map = parents => from parent in parents
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
ChildItemValues = parent.ChildItems.SelectMany(p => p.ChildItemValues.Select(y => y)).ToArray(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
Index("ParentItemId", FieldIndexing.Analyzed);
Index("ParentItemName", FieldIndexing.Analyzed);
Index("ParentItemValue", FieldIndexing.Analyzed);
Index("ChildItemValues", FieldIndexing.Analyzed);
Index("ChildItems", FieldIndexing.Analyzed);
}
}
my query is as follows , (this is using the live raven playground so this should just work out of the box it you want to use it)
using (IDocumentStore store = new DocumentStore { Url = "http://live-test.ravendb.net/", DefaultDatabase = "altha" })
{
store.Initialize();
using (IDocumentSession session = store.OpenSession())
{
if(1 == 2){
//foreach (ParentItem element in data.OfType<ParentItem>()) {
// session.Store((ParentItem)element);
// session.SaveChanges();
//}
new familyIndex().Execute(store);
new familyTransfrom().Execute(store);
}else{
double filterMinValue = 3.0;
double filterMaxValue = 4.0;
var results = session
.Advanced
.DocumentQuery<familyIndex.Result,familyIndex>()
.WhereBetweenOrEqual("ChildItemValues", filterMinValue, filterMaxValue)
.SetResultTransformer<familyTransfrom, familyTransfrom.Result>()
.SetTransformerParameters(new Dictionary<string, RavenJToken> {
{ "FilterMinValue", filterMinValue },
{ "FilterMaxValue", filterMaxValue } })
.OrderBy("ChildItemValues")
.OfType<ParentItem>().ToList();
results.Dump();
}}
}
What i found was i cant use "ChildItemValuesFiltered" from the transform result as its not index. So unless i can order by the result of a transform ? i couldn't get this to work as although it filters it dosnt order correctly.
Is there another to achieve what i want using projections or intersection or rank or reduce try method ?
I was thinking if i had to perhaps i could use https://ravendb.net/docs/article-page/3.5/csharp/indexes/querying/sorting#custom-sorting
and do something like this:
public class SortByNumberOfCharactersFromEnd : IndexEntriesToComparablesGenerator
{
private readonly double filterMinValue;
private readonly double filterMinValue;
public SortByNumberOfCharactersFromEnd(IndexQuery indexQuery)
: base(indexQuery)
{
filterMinValue = IndexQuery.TransformerParameters["FilterMinValue"].Value<double>(); // using transformer parameters to pass the length explicitly
filterMaxValue = IndexQuery.TransformerParameters["FilterMaxValue"].Value<double>();
}
public override IComparable Generate(IndexReader reader, int doc)
{
var document = reader.Document(doc);
double[] childItemValues = (double[])document.GetValues("ChildItemValuesFiltered").Select(double.Parse).ToArray(); // this field is stored in index
return childItemValues.Where(x => x >= min && x <= max).Min();
}
}
then do a where filter and order by clause using index and transform passing in the same prams that i use in the where filter . however im not sure if this would work ?
More importantly im not sure how i go about getting the sort dll into the plugins ie what name space should the class go under, what name spaces dose it need to import, what assembly name dose it need to use etc
According to https://ravendb.net/docs/article-page/3.5/csharp/server/plugins/what-are-plugins i just need to drop the dll in and raven will this this up , however i cant seem to find what name space i need to reference for IndexEntriesToComparablesGenerator ?
im using linqpad 5 to test my stuff ...so in order to use the custom order i have to reference the class
any tips or advice or how to guild/examples welcome
so it didn't occur to me that i could do the filtering in the transform
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new {
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
} into r
where r.ChildItemValuesFiltered.Length > 0
orderby r.ChildItemValuesFiltered.Min()
select r;
This gives me what i wanted, here are the sample query:
http://live-test.ravendb.net/databases/altha/indexes/familyIndex?start=0&pageSize=25&resultsTransformer=familyTransfrom&tp-FilterMinValue=3&tp-FilterMaxValue=4
i cant take credit for this as guys at raven helped me but sharing the knowledge for others
I am staring out with PHPUnit today. As i use Yii framework i am using the built in functions.
Can someone let me know if i am proceeding correctly
Here is the model function
public function getTaxRate()
{
if($this->province_id != 13 && $this->province_id != 14)
{
return 21;
}
elseif($this->identification[0] == 'B')
{
return 0;
}
else
{
return 7;
}
}
Here is the test case
public function testgetTaxRate()
{
$accountData = array(
array('identification'=>'x2', 'province_id'=>'50', 'result'=>21), // test for 21
array('identification'=>'x2', 'province_id'=>'13', 'result'=>7), // test for 7
array('identification'=>'B2', 'province_id'=>'13', 'result'=>0), // test for 0
);
foreach($accountData as $account)
{
$acc = new Accounts();
$acc->identification=$account['identification'];
$acc->province_id=$account['province_id'];
$tax = $acc->getTaxRate();
$this->assertEquals($tax, $account['result']);
}
}
Am I doing this correctly, The result is correct and it errors when I expect it to.
Regards
There is a good rule for organizing tests: one test per one case. If you have only one logic, you should use data provider mechanism (provided by PHPUnit) for this purpose in order to avoid code duplication.
Docs: http://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers
And an example for you:
/**
* #dataProvider dataProvider_getTaxRate
*/
public function testGetTaxRate($id, $provinceId, $expectedResult)
{
$acc = new Accounts();
$acc->identification = $id;
$acc->province_id = $provinceId;
$this->assertEquals($expectedResult, $acc->getTaxRate());
}
public static function dataProvider_getTaxRate()
{
return array(
array('x2', '50', 21),
array('x2', '13', 7),
array('x2', '14', 7),
array('B2', '13', 0),
);
}
One more thing - it's good habit that expectations should be at first argument in assertions.
I don't like using an indexed array for no reason other than I think it looks ugly. Is there a clean way to sum with an anonymous function? Is it possible to do it without using any outside variables?
Dart iterables now have a reduce function (https://code.google.com/p/dart/issues/detail?id=1649), so you can do a sum pithily without defining your own fold function:
var sum = [1, 2, 3].reduce((a, b) => a + b);
int sum = [1, 2, 3].fold(0, (previous, current) => previous + current);
or with shorter variable names to make it take up less room:
int sum = [1, 2, 3].fold(0, (p, c) => p + c);
This is a very old question but
In 2022 there is actually a built-in package.
Just import
import 'package:collection/collection.dart';
and call the .sum extension method on the Iterable.
FULL EXAMPLE
import 'package:collection/collection.dart';
void main() {
final list = [1, 2, 3, 4];
final sum = list.sum;
print(sum); // prints 10
}
If the list is empty, .sum returns 0.
You might also be interested in list.average...
I still think this is cleaner and easier to understand for this particular problem.
num sum = 0;
[1, 2, 3].forEach((num e){sum += e;});
print(sum);
or
num sum = 0;
for (num e in [1,2,3]) {
sum += e;
}
There is not a clean way to do it using the core libraries as they are now, but if you roll your own foldLeft then there is
main() {
var sum = foldLeft([1,2,3], 0, (val, entry) => val + entry);
print(sum);
}
Dynamic foldLeft(Collection collection, Dynamic val, func) {
collection.forEach((entry) => val = func(val, entry));
return val;
}
I talked to the Dart team about adding foldLeft to the core collections and I hope it will be there soon.
Starting with Dart 2.6 you can use extensions to define a utility method on the List. This works for numbers (example 1) but also for generic objects (example 2).
extension ListUtils<T> on List<T> {
num sumBy(num f(T element)) {
num sum = 0;
for(var item in this) {
sum += f(item);
}
return sum;
}
}
Example 1 (sum all the numbers in the list):
var numbers = [1, 2, 3];
var sum = numbers.sumBy((number) => number);
Example 2 (sum all the Point.x fields):
var points = [Point(1, 2), Point(3, 4)];
var sum = points.sumBy((point) => point.x);
I'd just like to add some small detail to #tmaihoff's answer (about using the collection.dart package):
The sum getter he talks about only works for iterables of num values, like List<int> or Set<double>.
If you have a list of other object types that represent values (like Money, Decimal, Rational, or any others) you must map it to numbers. For example, to count the number of chars in a list of strings you can do:
// Returns 15.
['a', 'ab', 'abc', 'abcd', 'abcde'].map((e) => e.length).sum;
As of 2022, another way of doing it, is using the sumBy() method of the fast_immutable_collections package:
// Returns 15.
['a', 'ab', 'abc', 'abcd', 'abcde'].sumBy((e) => e.length), 15);
Note: I'm the package author.
I suggest you to create this function in any common utility file.
T sum<T extends num>(T lhs, T rhs) => lhs + rhs;
int, double, float extends num class so you can use that function to sum any numbers.
e.g.,
List<int> a = [1,2,3];
int result = a.reduce(sum);
print(result); // result will be 6
Herewith sharing my Approach:
void main() {
int value = sumTwo([1, 4, 3, 43]);
print(value);
}
int sumTwo(List < int > numbers) {
int sum = 0;
for (var i in numbers) {
sum = sum + i;
}
return sum;
}
If when using fold gives a double TypeError, you can use reduce:
var sum = [0.0, 4.5, 6.9].reduce((a, b) => a + b);
If you are planning on doing a number of mathematical operations on your list, it may be helpful to create another list type that includes .sum() and other operations by extending ListBase. Parts of this are inspired by this response with performance tweaks from this response.
import 'dart:collection';
import 'dart:core';
class Vector<num> extends ListBase<num> {
List<num> _list;
Vector() : _list = new List<num>();
Vector.fromList(List<num> lst): _list = lst;
void set length(int l) {
this._list.length=l;
}
int get length => _list.length;
num operator [](int index) => _list[index];
void operator []=(int index, num value) {
_list[index]=value;
}
// Though not strictly necessary, for performance reasons
// you should implement add and addAll.
void add(num value) => _list.add(value);
void addAll(Iterable<num> all) => _list.addAll(all);
num sum() => _list.fold(0.0, (a, b) => a + b) as num;
/// add additional vector functions here like min, max, mean, factorial, normalize etc
}
And use it like so:
Vector vec1 = Vector();
vec1.add(1);
print(vec1); // => [1]
vec1.addAll([2,3,4,5]);
print(vec1); // => [1,2,3,4,5]
print(vec1.sum().toString()); // => 15
Vector vec = Vector.fromList([1.0,2.0,3.0,4.0,5.0]); // works for double too.
print(vec.sum().toString()); // => 15
A solution that has worked cleanly for me is:
var total => [1,2,3,4].fold(0, (e, t) => e + t); // result 10
Different ways to find the sum of all dart list elements,
Method 1: Using a loop :
This is the most commonly used method. Iterate through the list using a loop and add all elements of the list to a final sum variable. We are using one for loop here :
main(List<String> args) {
var sum = 0;
var given_list = [1, 2, 3, 4, 5];
for (var i = 0; i < given_list.length; i++) {
sum += given_list[i];
}
print("Sum : ${sum}");
}
Method 2: Using forEach :
forEach is another way to iterate through a list. We can also use this method to find out the total sum of all values in a dart list. It is similar to the above method. The only difference is that we don’t have to initialize another variable i and list.length is not required.
main(List<String> args) {
var sum = 0;
var given_list = [1, 2, 3, 4, 5];
given_list.forEach((e) => sum += e);
print("Sum : ${sum}");
}
Method 3: Using reduce :
reduce method combines all elements of a list iteratively to one single value using a given function. We can use this method to find out the sum of all elements as like below :
main(List<String> args) {
var given_list = [1, 2, 3, 4, 5];
var sum = given_list.reduce((value, element) => value + element);
print("Sum : ${sum}");
}
Method 4: Using fold :
fold() is similar to reduce. It combines all elements of a list iteratively to one single value using a function. It takes one initial value and calculates the final value based on the previous value.
main(List<String> args) {
var sum = 0;
var given_list = [1,2,3,4,5];
sum = given_list.fold(0, (previous, current) => previous + current);
print("Sum : ${sum}");
}
for more details:https://www.codevscolor.com/dart-find-sum-list-elements
extension DoubleArithmeticExtensions on Iterable<double> {
double get sum => length == 0 ? 0 : reduce((a, b) => a + b);
}
extension IntArithmeticExtensions on Iterable<int> {
int get sum => length == 0 ? 0 : reduce((a, b) => a + b);
}
Usage:
final actual = lineChart.data.lineBarsData[0].spots.map((s) => s.x).sum;
I have the following 3 tables as part of a simple "item tagging" schema:
==Items==
ItemId int
Brand varchar
Name varchar
Price money
Condition varchar
Description varchar
Active bit
==Tags==
TagId int
Name varchar
Active bit
==TagMap==
TagMapId int
TagId int (fk)
ItemId int (fk)
Active bit
I want to write a LINQ query to bring back Items that match a list of tags (e.g. TagId = 2,3,4,7). In my application context, examples of items would be "Computer Monitor", "Dress Shirt", "Guitar", etc. and examples of tags would be "electronics", "clothing", etc. I would normally accomplish this with a SQL IN Statement.
Something like
var TagIds = new int[] {12, 32, 42};
var q = from map in Context.TagMaps
where TagIds.Contains(map.TagId)
select map.Items;
should do what you need. This will generate an In ( 12, 32, 42 ) clause (or more specifically a parameterized IN clause if I'm not mistaken).
given array of items:
var list = new int[] {2,3,4}
use:
where list.Contains(tm.TagId)
List<int> tagIds = new List<int>() {2, 3, 4, 7};
int tagIdCount = tagIds.Count;
//
// Items that have any of the tags
// (any item may have any of the tags, not necessarily all of them
//
var ItemsAnyTags = db.Items
.Where(item => item.TagMaps
.Any(tm => tagIds.Contains(tm.TagId))
);
//
// Items that have ALL of the tags
// (any item may have extra tags that are not mentioned).
//
var ItemIdsForAllTags = db.TagMap
.Where(tm => tagIds.Contains(tm.TagId))
.GroupBy(tm => tm.ItemId)
.Where(g => g.Count() == tagIdCount)
.Select(g => g.Key);
//
var ItemsWithAllTags = db.Items
.Where(item => ItemsIdsForAllTags.Contains(item.ItemId));
//runs just one query against the database
List<Item> result = ItemsWithAllTags.ToList();
You can simply use,
var TagIds = {12, 32, 42}
var prod =entities.TagMaps.Where(tagmaps=> TagIds .Contains(tagmaps.TagId));
string[] names = {"John", "Cassandra", "Sarah"};
var results = (from n in db.Names
where names.Contains(n.Name)
select n).ToList();
You may create an extension method "IN()"
public static class Extension
{
public static bool IN(this object anyObject, params object[] list)
{ return list.Contains(anyObject); }
}
to be used like this
var q = from map in Context.TagMaps
where map.TagId.IN(2, 3, 4, 7)
select map.Items;
Or just use the inline array.Contains() notation:
var q = from map in Context.TagMaps
where new[]{2, 3, 4, 7}.Contains(map.TagId)
select map.Items;