LINQ - OrderBy explictly - linq

Is it possible to use LINQ OrderBy like so:
.OrderBy(x=>(x.SourceID == 3), (x.SourceID == 2), (x=>x.SourceID == 4), (x.SourceID == 1)).ToList();
So it'll order them by 3, 2, 4, 1 ?

No, that is not a valid lambda expression. What you could do instead is something like;
var sortOrder = new List<int> {3, 2, 4, 1};
var result = bop.OrderBy(x=> sortOrder.IndexOf(x.SourceID)).ToList();
If you want to extend this to doing special things with unknowns (they end up first now), you can just make a method that makes the determination of the sort order and use that instead.

Taking Joachim Isaksson's answer above, this could be wrapped in an extension method:
public static class ListExtensions
{
public static List<Source> SortByCustomOrder(this List<Source> list, List<int> sortOrder)
{
return list.OrderBy(x => sortOrder.IndexOf(x.SourceId)).ToList();
}
}
replacing Source with your Class and x.SourceId with your property
Usage:
// the sort order
var sortOrder = new List<int> { 3, 2, 4, 1, 6, 5 };
var results = sources.SortByCustomOrder(sortOrder);

Related

Java/Kotlin - convert Set to Map<Long, Set<Long>>

I have Set of Long values
Set<Long> ids = {1,2,3,4}
What I'd like to achieve is
Set<Map<Long, Set<Long>>
and from this Set of ids I need to have Set with 4 elements like:
Set: {
Map -> key: 1, values: 2,3,4
Map -> key: 2, values: 1,3,4
Map -> key: 3, values: 1,2,4
Map -> key: 4, values: 1,2,3
}
How can i get it by stream or maybe kotlin's groupBy ?
Was anyone going to have a map like this? (Solution without a for or while loop)
You can use use map method to transform every element to Map then collect it to set
var set = setOf(1, 2, 3, 4)
var map = set.map { v -> mapOf(v to set.filter { it != v }.toSet()) }
.toSet()
However I don't believe it's much better than simple foreach loop due to performance or readability
Opinions on kotlin groupBy
Notice that groupBy can just split the original set into severial sets without intersection. So it's impossible to construct the mentioned map directly with groupBy function.
The solution below take advantage of groupBy when getting result, but result2 is much more clear to read and meets intuition:
fun main() {
val set = setOf(1, 2, 3, 4)
val result = set
.groupBy { it }
.mapValues { (_, values) -> set.filter { it !in values } }
println(result) // {1=[2, 3, 4], 2=[1, 3, 4], 3=[1, 2, 4], 4=[1, 2, 3]}
val result2 = HashMap<Int, List<Int>>().apply {
set.forEach { this[it] = (set - it).toList() }
}
println(result2) // {1=[2, 3, 4], 2=[1, 3, 4], 3=[1, 2, 4], 4=[1, 2, 3]}
}
That would be a possible solution with a for loop:
val ids: Set<Long> = setOf(1, 2, 3, 4)
var result: MutableSet<Map<Long, Set<Long>>> = mutableSetOf()
for (id in ids) {
result.add(mapOf(id to ids.filter { it != id }.toSet()))
}
println(result)

How to split a stream into multiple stream based on certain criteria?

I have a list of integers and I want to return sub lists of integers from this list using java 8 streams where my sub list contain all the positive integers from original list just before it encounters a negative integer
For ex. let's say my list have elements
[1 , 2 , 0 , -1 , 5 , 8 , 9 , -11 , 7 , 13 ]
then I should return sub lists containing
[1 , 2 , 0 ] [ 5 , 8 , 9 ] [7 , 13]
I tried following approach but it's not working, your direction & input is much appreciated.
package Java8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public class StreamsExamples {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(1);
arr.add(2);
Integer[] al = new Integer[]{0,-1,5,8,9,-11,7,13};
arr.addAll(Arrays.asList(al));
arr.stream().collect(Collectors.groupingBy(Functionality::getPositiveList));
// arr.stream().collect(Collectors.toList()).;
//arr.stream().sorted().forEach(System.out::print);
}
}
class Functionality{
public List<List<Integer>> getPositiveList(List<Integer> list){
List<List<Integer>> li = new ArrayList<List<Integer>>();
List<Integer> a = new ArrayList<Integer>();
for(int i=0;i<list.size();i++) {
if(list.get(i)>=0) {
a.add(list.get(i));
}
else if (list.get(i)<0) {
li.add(a);
a.clear();
}
}
return li;
}
}
This isn't so hard if you think a bit different here: find out the indexes where the negative value is and just do a subList between those... There are some quirks to do with IntStream.of(-1) (but I'll let you figure it out why it is like that: try to replace it with the more intuitive IntStream.of(0) and see what is going on). So having an input like:
ArrayList<Integer> arr = List.of(1, 2, 0, -1, 5, 8, 9, -11, 7, 13);
You first find out the indexes:
int[] indexes = IntStream.concat(
IntStream.of(-1),
IntStream.concat(
IntStream.range(0, arr.size())
.filter(x -> arr.get(x) < 0),
IntStream.of(arr.size())))
.toArray();
System.out.println(Arrays.toString(indexes));
This will give a result like: [-1, 3, 7, 10].
Thus just compute the subList between these:
IntStream.range(0, indexes.length - 1)
.mapToObj(x -> arr.subList(indexes[x] + 1, indexes[x + 1]))
.collect(Collectors.toList())
.forEach(System.out::println);
It's a bit ugly, but this works:
List<List<Integer>> lists = Arrays.stream(arr).boxed()
.reduce(
new ArrayList<>(),
(l, i) -> {
if (l.isEmpty() || i < 0) {
l.add(new ArrayList<>());
}
if (i >= 0) {
l.get(l.size() - 1).add(i);
}
return l;
}, (a, b) -> {
a.addAll(b);
return a;
});
Tested:
int [] arr = {1, 2, 0, -1, 5, 8, 9, -11, 7, 13};
List<List<Integer>> lists = Arrays.stream(arr).boxed().reduce(new ArrayList<>(),
(l, i) -> {
if (l.isEmpty() || i < 0) {
l.add(new ArrayList<>());
}
if (i >= 0) {
l.get(l.size() - 1).add(i);
}
return l;
}, (a, b) -> {
a.addAll(b);
return a;
});
System.out.println(lists);
Output:
[[1, 2, 0], [5, 8, 9], [7, 13]]

Behaviour of each iterator in ruby

Why does this block of code output [1, 2, 3, 4, 5] and not [2, 3, 4, 5, 6]?
x = [1, 2, 3, 4, 5]
x.each do |a|
a + 1
end
I viewed the source of each at https://ruby-doc.org/core-2.2.0/Array.html#method-i-each. Something like this is written there.
VALUE
rb_ary_each(VALUE array)
{
long i;
volatile VALUE ary = array;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}
Can someone please explain?
It outputs the object, you're calling each on, because this is the return value of each.
If you want to just print the a + 1, you should actually make it being output:
x.each do |a|
puts a + 1
end
Or, if your desired result is [2, 3, 4, 5, 6] - you want Enumerable#map, not each.
x.map { |a| a + 1 }
#=> [2, 3, 4, 5, 6]
Let me go through the key lines.
From this one it follows 'ary' is logically equal to array. Note the line is absent from newer versions of Ruby such as 2.4.0!
volatile VALUE ary = array;
I skip RETURN_SIZED_ENUMERATOR since a block is given. Refer to its source in at include/ruby/intern.h.
Next, we go into a 'for' for each element of 'ary' array.
Next is the line that puzzles you, I believe. First, it take i-th element from 'ary' array via RARRAY_AREF macro. Second, it passed the element's value to the block given (i.e 'a + 1') via rb_yield. Thus, it does not store anything.
rb_yield(RARRAY_AREF(ary, i));
Since nothing was written at rb_yield, the function return the 'ary' array which is [see above] is input 'array'.
Comparing it to 'map!' may help you further:
static VALUE rb_ary_collect_bang(VALUE ary)
{
long i;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
rb_ary_modify(ary);
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
}
return ary;
}
Note 'rb_ary_store' function call inside the 'for' loop. It is the thing! It rb_yield-s just like in 'each' variant, but it does not throw away the result returned. The result is stored at i-th element of our [beloved] 'ary' array.

Find optimal array intersections

I have an array of arrays and a matching array. Each array has unique id values.
MatchingArray = [1,2,3,4,5,6]
A1 = [1, 4, 6]
A2 = [2,3,5]
A3 = [1,5]
A4 = [4]
A5 = [1, 6]
Need to find "optimal matchings". An optimal matching is an array of subsets from A1-A5 with minimal length, which should have a maximum possible intersection with MatchingArray.
For this example there are 2 possible matchings with a maximum intersection: M1 = [[2,3,5], [1, 4, 6]] and M2 = [[1,5], [4], [1, 6]]. But M1.length < M2.length, so the algorithm should output M1.
You could use sets (or hashes, whatever the language calls them) to optimise the time efficiency.
Convert the target array to a set, and then subtract the selected source from it (i.e. removing common values). Keep doing this recursively until the target set is empty. Keep track of the best result (using the fewest source arrays as possible). Backtrack if the number of source arrays being used gets past the length of the best solution already found at that moment.
Here is the code in Python:
def find_optimal_coverage(target, sources):
max_size = len(target)
best = None
def recurse(target, sources, selected):
nonlocal max_size, best
if len(target) == 0:
best = selected
max_size = len(best) - 1
return True
if len(selected) == max_size:
return None
for i, source in enumerate(sources):
result = recurse(target - set(source), sources[i+1:],
selected + [list(source)])
if result:
return True
target = set(target) # convert to set for faster lookup
# limit the source lists to elements that occur in the target
sources = list(map(target.intersection, sources))
# limit target to elements that occur in at least one source
target = set.union(*sources)
# sort sources by decreasing length to maximise probability of
# finding optimal solution sooner
sources.sort(key = len, reverse = True)
if recurse(target, sources, []):
return best
result = find_optimal_coverage(
[1, 2, 3, 4, 5, 6, 8],
[
[1, 4, 6, 7],
[2, 3, 5],
[1, 5],
[4],
[1, 6]
]
)
print(result)
See it run on repl.it
In JavaScript:
function subtractArray(s, arr) {
return arr.reduce( (s, v) => (s.delete(v), s), new Set(s) );
}
function findOptimalCoverage(target, sources) {
var maxSize = target.size;
var best = null;
function recurse(target, sources, selected) {
if (target.size == 0) {
best = selected;
maxSize = best.length - 1;
return true;
}
if (selected.length == maxSize) return;
return sources.some( (source, i) =>
recurse(subtractArray(target, source), sources.slice(i+1),
selected.concat([source]))
);
}
target = new Set(target) // convert to set for faster lookup
// limit the source arrays to elements that occur in the target
sources = sources.map( source => source.filter(target.has.bind(target)));
// limit target to elements that occur in at least one source
target = new Set([].concat(...sources));
// sort sources by decreasing length to maximise probability of
// finding optimal solution sooner
sources.sort( (a,b) => b.length - a.length );
if (recurse(target, sources, [])) return best;
}
var result = findOptimalCoverage(
[1, 2, 3, 4, 5, 6, 8],
[
[1, 4, 6, 7],
[2, 3, 5],
[1, 5],
[4],
[1, 6]
]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Implemented algorithm in javascript:
var matchingArray = [1, 2, 3, 4, 5, 6];
var A1 = [1, 4, 6],
A2 = [2, 3, 5],
A3 = [1, 5],
A4 = [4],
A5 = [1, 6];
var M = [A1, A2, A3, A4, A5];
function compareArrays(M, machingArray) {
var intersections = []
M.forEach(function(A) {
var partOfItersections;
if (A.length > 0) {
var intersectionsCount = getIntersectionCount(A, machingArray);
partOfItersections = intersectionsCount / A.length;
} else {
partOfItersections = 0
}
intersections.push({
length: A.length,
partOfItersections: partOfItersections
});
});
//alert(JSON.stringify(intersections));
var maxLength = 0,
maxPartOfItersections = 0,
optimalArrays = [];
intersections.forEach(function(arrayData, index) {
var currentArr = M[index];
var currentArrLength = currentArr.length;
if (maxPartOfItersections < arrayData.partOfItersections) {
setCurrentOptimalArr(arrayData.partOfItersections, currentArr);
} else if (maxPartOfItersections === arrayData.partOfItersections) {
if (maxLength < currentArrLength) {
setCurrentOptimalArr(arrayData.partOfItersections, currentArr);
} else if (maxLength === currentArrLength) {
optimalArrays.push(currentArr);
}
}
});
//alert(JSON.stringify(optimalArrays));
return optimalArrays;
function setCurrentOptimalArr(intersectionsCount, currentArr) {
optimalArrays = [currentArr];
maxLength = currentArr.length;
maxPartOfItersections = intersectionsCount;
}
function getIntersectionCount(A, machingArray) {
var intersectionCount = 0;
A.forEach(function(elem) {
if (machingArray.indexOf(elem) != -1) {
intersectionCount++;
}
});
return intersectionCount;
}
}
alert(JSON.stringify(compareArrays(M, matchingArray)));
Count intersection of arrays separately.
Return arrays which contain more part of intersections.
Code updated

Linq intersect with sum

I have two collections that I want to intersect, and perform a sum operation on matching elements.
For example the collections are (in pseudo code):
col1 = { {"A", 5}, {"B", 3}, {"C", 2} }
col2 = { {"B", 1}, {"C", 8}, {"D", 6} }
and the desired result is:
intersection = { {"B", 4}, {"C", 10} }
I know how to use an IEqualityComparer to match the elements on their name, but how to sum the values while doing the intersection?
EDIT:
The starting collections haven't two items with the same name.
Let's say your input data looks like this:
IEnumerable<Tuple<string, int>> firstSequence = ..., secondSequence = ...;
If the strings are unique in each sequence (i.e there can be no more than a single {"A", XXX} in either sequence) you can join like this:
var query = from tuple1 in firstSequence
join tuple2 in secondSequence on tuple1.Item1 equals tuple2.Item1
select Tuple.Create(tuple1.Item1, tuple1.Item2 + tuple2.Item2);
You might also want to consider using a group by, which would be more appropriate if this uniqueness doesn't hold:
var query = from tuple in firstSequence.Concat(secondSequence)
group tuple.Item2 by tuple.Item1 into g
select Tuple.Create(g.Key, g.Sum());
If neither is what you want, please clarify your requirements more precisely.
EDIT: After your clarification that these are dictionaries - your existing solution is perfectly fine. Here's another alternative with join:
var joined = from kvp1 in dict1
join kvp2 in dict2 on kvp1.Key equals kvp2.Key
select new { kvp1.Key, Value = kvp1.Value + kvp2.Value };
var result = joined.ToDictionary(t => t.Key, t => t.Value);
or in fluent syntax:
var result = dict1.Join(dict2,
kvp => kvp.Key,
kvp => kvp.Key,
(kvp1, kvp2) => new { kvp1.Key, Value = kvp1.Value + kvp2.Value })
.ToDictionary(a => a.Key, a => a.Value);
This will give the result, but there are some caveats. It does an union of the two collections and then it groups them by letter. So if, for example, col1 contained two A elements, it would sum them together and, because now they are 2 A, it would return them.
var col1 = new[] { new { L = "A", N = 5 }, new { L = "B", N = 3 }, new { L = "C", N = 2 } };
var col2 = new[] { new { L = "B", N = 1 }, new { L = "C", N = 8 }, new { L = "D", N = 6 } };
var res = col1.Concat(col2)
.GroupBy(p => p.L)
.Where(p => p.Count() > 1)
.Select(p => new { L = p.Key, N = p.Sum(q => q.N) })
.ToArray();
The best I came up with until now is (my collections are actually Dictionary<string, int> instances):
var intersectingKeys = col1.Keys.Intersect(col2.Keys);
var intersection = intersectingKeys
.ToDictionary(key => key, key => col1[key] + col2[key]);
I'm not sure if it will perform well, at least is it readable.
If your intersection algorithm will result in anonymous type, i.e. ...Select(new { Key = key, Value = value}) then you can easily sum it
result.Sum(e => e.Value);
If you want to sum the "while" doing the intersection, add the value to the accumulator value when adding to the result set.

Resources