I'm trying to find an elegant way to lexically sort sequences of integers with LINQ. In other words, If I have these sequences of ints
7, 10, 12, 14, 15
10, 12, 15
10
7, 15
14, 15
I would hope to have them come out sorted like this
7, 10, 12, 14, 15
7, 15
10
10, 12, 15
14, 15
It is the same basic idea of sorting strings by their characters except I want to sort a sequence of ints instead. I don't want to sort alphabetically, but I do want the sequences lexically sorted.
The kind of sort you want is known as a lexical sort:
Given two partially ordered sets A and B, the lexicographical order on
the Cartesian product A × B is defined as (a,b) ≤ (a′,b′) if and only
if a < a′ or (a = a′ and b ≤ b′).
.Net gives you the tools to specify what kind of comparison you want when you want to sort. There are two structures to control this: IComparer and the Comparison<T> delegate. You can pass either one of these to List.Sort. Example:
var lists = new List<Int32[]> {
new [] { 7, 10, 12, 14, 15 },
new [] { 7, 15 },
new [] { 7, 15 },
new [] { 10 },
new [] { 10, 12, 15 },
new [] { 14, 15 } };
lists.Sort((a, b) => {
var result = a.Zip(b, Tuple.Create)
.Select(t => t.Item1.CompareTo(t.Item2))
.FirstOrDefault(c => c != 0);
return result == 0 && !a.Any() ? -1 : result; // Empty list minimum
});
(Download for LinqPad)
This passes a Comparison<Int32[]> delegate which zips the comparands, allowing element by element comparison and stops comparing when the first non-zero integer comparison is detected. If no unequal elements are found, it returns the default for Int32 which is 0, meaning the lists are lexicographically equal.
(Note I added another element to your set of lists to show that equal lists of integers sort correctly.)
Bonus chatter:
I thought this method would be faster than allocating strings and using OrderBy but after profiling there's no appreciable difference in speed. I tried to make it faster using a struct instead of Tuple, which did help a little, and probably saved GC allocation and memory usage, but I didn't measure memory usage performance. If performance is a concern, you'd probably end up eschewing the tidy Linq approach and write the Comparer using a loop.
You could convert the numbers to strings, pad them all to the same length and join the together, then order that, and finally split them up again.
var intLists = new List<List<int>>
{
new List<int> { 7, 10, 12, 14, 15 },
new List<int> { 10, 12, 15 },
new List<int> { 10 },
new List<int> { 7, 15 },
new List<int> { 14, 15 },
};
var orderedLists = intLists
.Select(l => string.Join("", l.Select (x => x.ToString().PadLeft(10))))
.OrderBy(l => l)
.Select(l => l.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select (x => int.Parse(x)));
Related
I am looking in Kotlin for a method who return me from a list a new list with a defined number of elements (for example 10).
Whatever the size of the list, the method would always return the same number of elements.
For example, suppose a list of 3000 elements, it would return me a list of 10 elements from indexes 0, 300, 600, 900, 1200,...
Is there an extension function for this?
That's kind of a specialised thing, so there's nothing (that I know of) in the standard library - but you could easily make your own extension function:
fun <T: Any> List<T>.sample2(count: Int): List<T> {
// this allows for a fractional step, so we get a more accurate distribution of indices
// with smaller lists (where count doesn't divide into the list size evenly)
val step = size / count.toFloat()
return List(count) { i -> elementAt((i * step).toInt()) }
}
You'll get repeats if your list is too small to provide count unique indices (e.g. your list has 9 items and you want 10), so you'd have to handle that if you want different behaviour, but I think this is the easiest way to do it
Here's an idea:
Take advantage of the method chunked(size: Int), which tries to depart a given collection into sub-collections of the given size.
That's not quite what you want, but you can use it in order to implement a custom extension function which does what you want, e.g. like this:
fun List<Int>.departInto(subListCount: Int) : List<List<Int>> {
// calculate the chunk size based on the desired amount of sublists
val chunkSize = this.size / subListCount
// then apply that value to the chunked method and return the result
return this.chunked(chunkSize)
}
Using this could look as follows:
fun main() {
// define some example list (of 30 elements in this case)
val someList: List<Int> = List(30, {it})
// use the extension function
val tenSubLists = someList.departInto(10)
// print the result(s)
println(tenSubLists)
}
The output of this code will be 10 sub-lists of 3 elements (your example of 3000 elements would then result in 10 sub-lists of 300 elements each):
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19, 20], [21, 22, 23], [24, 25, 26], [27, 28, 29]]
I'm struggling with codewars kata called Range Extraction - that it takes a list of integers in increasing order and returns a correctly formatted string in the range format(overlapping seperate intervals).
Example solution:
([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
// returns "-6,-3-1,3-5,7-11,14,15,17-20"
Well in my solution, instead of getting -6,-3-1,3-5,7-11,14,15,17-20, I got the last item -6,1,5,11,15,20.
How can I enhance my solution? The code:
function solution(list){
let result=[]
for(let i=0;i<list.length;i++){
let e2=list[i]
let e1 = result[result.length-1]
if(e2-e1==1){
result[result.length-1]=e2
}
else{
result.push(e2 )
}
}
return result
}
console.log(solution([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]))
You are doing nothing to write consecutive integers in range format. Instead you are just replacing the previous result with the final item in the range which is exactly reflected in your solution:
-6: this number has no "neighbors" so is fine
1: the final item in the first range
5: the final item in the second range
...
the problem is the internal logic of the loop.
In summary, you need a while instead of an if and you need to append instead of replace:
function solution(list){
let result=[]
for(let i=0;i<list.length;i++){
//write first value in range to result
result.push(list[i].toString())
//if this is the last entry, we are done
if(i === list.length - 1){
break
}
//initialize variables
let e1 = list[i]
let e2 = list[i+1]
let isRange = false
//run thorugh array while we get consecutive numbers
while(e2-e1===1 && i < list.length-1){
//modify the OUTER LOOP index variable.
//This means when we return to the beginning of hte for loop,
// we will be at the beginning of the next range
i++
e1 = list[i]
e2 = list[i+1]
isRange = true
}
//if there were any consecutive numbers
if(isRange){
//rewrite the last entry in result as a range
result[result.length-1]+="-" + list[i].toString()
}
}
return result.toString()
}
console.log(solution([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]))
now, your outer loop runs through the entire array once. The inner loop will make sure the outer loop skips any items in the list that appear in a range. Finally, if the inner loop found any range at all, it will rewrite the entry as the correct range.
A LINQ challenge:
I have a ordered list of numbers. assume { 2 , 3 , 5, 7, 11 }
I like to calculate the average between each item and the next.
Is there any short way to do this with linq?
so the result in this case should be :
{ 2.5 , 4, 6, 9 }
You can use Enumerable.Zip:
List<int> ints = new List<int> { 2, 3, 5, 7, 11 };
IEnumerable<double> averages = ints.Zip(ints.Skip(1), (i1, i2) => (i1 + i2) / 2d);
This zips the list with itself (starting with the second element due to Skip(1)).
The result: 2.5 , 4.0, 6.0, 9.0
Apple's newly released language Swift has an example on the official documentation. Example is like this;
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest
This is pretty simple but as an extra exercise,it requires to add another variable in order to return what type is the largest number (i.e. Square is the case here)
However, I can't seem to figure out what is "(kind,numbers)" here represent and how should I make my for-loop to go through all Dictionary(interestingNumbers) keys and find which key has the largest number.
Thank you all for your help in advance
Swift allows you to loop over a dictionary with tuple-syntax (key, value). So in every iteration of the for-loop Swift cares about reassigning the specified tuple-variables (kind and number in your case) to the actual dictionary-record.
To figure out which Key includes the highest number in your example you can extend your code as follows:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestKey = ""
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
largestKey = kind
}
}
}
largest // =25
largestKey // ="Square"
Or if you want to practice the tuple-syntax try that (with the same result):
var largest = 0
var largestKey = ""
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
(largestKey, largest) = (kind, number)
}
}
}
largest // =25
largestKey // ="Square"
I can't seem to figure out what is "(kind,numbers)" here represents
It's a key-value pair (a tuple) containing the kind of the number. This syntax is called decomposition, basically, inside the loop you can access kind as the kind and numbers as the numbers that map for it.
For example, in some iteration:
kind // "Prime"
numbers // [2, 3, 5, 7, 11, 13]
Quoting the guide:
You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within in the body of the for-in loop.
for (kind, numbers) in interestingNumbers{}
This for loop actually enumerating the key/value pairs of dictionary interestingNumbers. Where kind is the key and numbers is the correspoding value
kind:Prime //Key
numbers: [2, 3, 5, 7, 11, 13] //Value
Here the complete solution of the exercise
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var type: String = ""
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
type = kind
}
}
}
largest
type
However, I can't seem to figure out what is "(kind,numbers)" here represent
The loop iterates over the dictionary, and every iteration gives you a key and associated value. Those are called kind (key) and numbers (value) here. You can choose any name you want.
and how should I make my for-loop to go through all Dictionary(interestingNumbers) keys and find which key has the largest number.
You get each key in turn in the kind loop variable.
Once you find one that results in a new largest, you can assign that to a result variable, say largestKind.
At the end of the loop, largestKind will contain the key of the array with the largest number (that number being the largest you already have).
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest
This will return pair of (String,Int) which we have in Our Dictionary
similar to function return multiple value as below,
func refreshWebPage() -> (status:String,code:Int){
//refresh logic
return ("Success",200)
}
Reference object: { 1, 5, 6, 9, 10, 11 }
Other objects:
A { 2, 4, 5, 6, 8, 10, 11 }
B { 5, 7, 9, 10 }
C { 2, 5, 6, 7, 9, 12 }
D { 1, 3, 4, 5, 6, 8, 9, 10 }
E { 6, 8 }
F { 1, 2, 3, 4, 7, 8, 9, 13, 15 }
... { ... }
Difficulty: It should be faster than O(n*m)
Result should be:
Array
(
[D] => 5
[A] => 4
[C] => 3
[B] => 3
[F] => 2
[E] => 1
)
Slow solution:
ref = array(1, 5, 6, 9, 10, 11);
foreach (A, B, C, D,.. AS row)
{
foreach (row AS col)
{
if ( exist(col, ref) )
{
result[row] += 1;
}
}
}
sort (result)
.. this is a solution, but its far to slow.
Is there another way like patter recognition, hopefully in O(log n)?
It is possible to save each object in an other notation, like for example:
ref = "15691011"
A = "2456811"
But I don't know if this helps.
If you have all data in your objects sorted, you can do this routine faster, by comparing not single values in the row, but whole row step by step.
foreach (A, B, C, D,.. AS row)
{
for (i = 0, j = 0; i < row.length && j < ref.length)
{
if (row[i] < ref[j]) i++;
elseif (row[i] > ref[j]) j++;
else {
result[row] += 1;
i++; j++;
}
}
}
In this case you pass you reference only once for each row, but this algorithm need all your data to be already sorted.
You could start with the largest sequence (it has the largest change to have many references).
When you find - for example - 4 refs, you can safely skip all sequences with less then 4 elements.
Another early exit is to abort checking a sequence, when the current sequence cannot surpass the current max. for example: Your current max is 6 elements. You are processing a list of size 7, but the first two elements are no reference. The highest reachable for this list is 5, which is lower than 6, abort the sequence.
Problem in both cases is that you can not construct a complete array of results.
Assumptions:
There are m lists apart from the reference object.
The lists are sorted initially.
There are no repetition of elements in any array.
Scan all the arrays and find out the maximum element in all the lists. You only need to check the last element in each list. Call it MAX.
For each of the m + 1 lists, make a corresponding Boolean array with MAX elements and initialize their values to zero.
Scan all the arrays and make the corresponding indices of arrays 1.
For example, the corresponding array for the example reference object { 1, 5, 6, 9, 10, 11 } shall look like:
{1,0,0,0,1,1,0,0,1,1,1,0,0,...}
Now for every pair-wise combination, you can just check the corresponding indices and increment the count if both are 1.
The above algorithm can be done in linear time complexity with regards to the total number of elements in the data.
You should use other techniques used in search engines. For each number, you have a list of object contained this number in sorted order. In your case
1 -> {D, F}
5 -> {A, B, C, D}
6 -> {A, C, D, E}
9 -> {B, C, D, F}
10 -> {A, B, D}
11 -> {A}
Merging this list you can count how your object is similar to objects in list
A -> 4
B -> 3
C -> 2
D -> 5
E -> 1
F -> 2
After sorting, you get needed result. If you need only top k elements, you should use a priority queue.