通过创建单个值按两个整数字段(一个 desc 和一个 asc)排序
Sorting by two integer fields, one desc and one asc, by creating a single value
我收集了以下对象:
public class TestObject
{
public string Id { get; set;}
public int Value1 { get; set; }
public int Value2 { get; set; }
}
如您所见,它包含两个整数值。我想按 Value1
降序然后 Value2
升序对集合进行排序。
为清楚起见,在 SQL:
SELECT *
FROM testobjects
ORDER BY Value1 DESC
ORDER BY Value2 ASC
或者如果您愿意,在 c# linq 中:
testObjects
.OrderByDescending(o => o.Value1)
.ThenBy(o => o.Value2);
但问题是...我需要通过将两个数字组合成一个数值来完成此操作,然后可以对其进行排序。 (为什么?CosmosDB,但这不是重点!)
我突然想到,如果我们采用 (Value1 x 1000) - Value2
然后按降序排列,前提是 Value2
不超过我们任意的 1000 那么这可能会起作用。
会吗?当然有更好的方法...?
SxS' 的字典序中的枚举,其中 S 是 int
的有序值集,S' 是 int
的值集,顺序相反,由下式给出
w((x,y)) = (x + min_S)·|S| + (max_S - y)
其中x,y是int
,min_S是最小的int
,max_S是最大的int
。
下面是如何比较 Value1
和 Value2
,两者都是升序的。
- 将
Value1
放入一个64位整数的高32位。
- 翻转
Value2
上的高位(符号位),并将其放入低32位。
例如:
public static long getKey(int val1, int val2)
{
long key = val1;
key <<= 32;
uint flipped = (uint)val2 ^ 0x80000000;
key |= flipped;
return key;
}
这将对 Value1
和 Value2
进行升序排序。但是您希望 Value1
降序排序。所以只需翻转第一个值中的所有位。将第一行更改为:
long key = (uint)~val1;
那么这是如何工作的呢?我将使用范围为 -8 .. 7
的 4 位有符号整数进行解释。高位是符号位。所以从低到高的表示是:
Bit Unsigned
Binary Inverted Value Flipped Value
-8 1000 0111 7 0000 0
-7 1001 0110 6 0001 1
-6 1010 0101 5 0010 2
-5 1011 0100 4 0011 3
-4 1100 0011 3 0100 4
-3 1101 0010 2 0101 5
-2 1110 0001 1 0110 6
-1 1111 0000 0 0111 7
0 0000 1111 -1 1000 8
1 0001 1110 -2 1001 9
2 0010 1101 -3 1010 10
3 0011 1100 -4 1011 11
4 0100 1011 -5 1100 12
5 0101 1010 -6 1101 13
6 0110 1001 -7 1110 14
7 0111 1000 -8 1111 15
第一列是数字。下一列显示二进制补码表示。 "Inverted" 列显示反转位的二进制结果,下一列显示表示的十进制值。这是我们放置在结果高位的值。如您所见,数字已经交换位置,最低 (-8) 变为最高。这将为我们提供降序排序的值。
除非高位相等,否则低位无关紧要。 "Bit Flipped" 列显示了翻转高位后值的二进制表示。这样做是将数字 -8 到 7 映射到无符号范围 0 到 15,从而确保正确比较数字的低位。
我收集了以下对象:
public class TestObject
{
public string Id { get; set;}
public int Value1 { get; set; }
public int Value2 { get; set; }
}
如您所见,它包含两个整数值。我想按 Value1
降序然后 Value2
升序对集合进行排序。
为清楚起见,在 SQL:
SELECT *
FROM testobjects
ORDER BY Value1 DESC
ORDER BY Value2 ASC
或者如果您愿意,在 c# linq 中:
testObjects
.OrderByDescending(o => o.Value1)
.ThenBy(o => o.Value2);
但问题是...我需要通过将两个数字组合成一个数值来完成此操作,然后可以对其进行排序。 (为什么?CosmosDB,但这不是重点!)
我突然想到,如果我们采用 (Value1 x 1000) - Value2
然后按降序排列,前提是 Value2
不超过我们任意的 1000 那么这可能会起作用。
会吗?当然有更好的方法...?
SxS' 的字典序中的枚举,其中 S 是 int
的有序值集,S' 是 int
的值集,顺序相反,由下式给出
w((x,y)) = (x + min_S)·|S| + (max_S - y)
其中x,y是int
,min_S是最小的int
,max_S是最大的int
。
下面是如何比较 Value1
和 Value2
,两者都是升序的。
- 将
Value1
放入一个64位整数的高32位。 - 翻转
Value2
上的高位(符号位),并将其放入低32位。
例如:
public static long getKey(int val1, int val2)
{
long key = val1;
key <<= 32;
uint flipped = (uint)val2 ^ 0x80000000;
key |= flipped;
return key;
}
这将对 Value1
和 Value2
进行升序排序。但是您希望 Value1
降序排序。所以只需翻转第一个值中的所有位。将第一行更改为:
long key = (uint)~val1;
那么这是如何工作的呢?我将使用范围为 -8 .. 7
的 4 位有符号整数进行解释。高位是符号位。所以从低到高的表示是:
Bit Unsigned
Binary Inverted Value Flipped Value
-8 1000 0111 7 0000 0
-7 1001 0110 6 0001 1
-6 1010 0101 5 0010 2
-5 1011 0100 4 0011 3
-4 1100 0011 3 0100 4
-3 1101 0010 2 0101 5
-2 1110 0001 1 0110 6
-1 1111 0000 0 0111 7
0 0000 1111 -1 1000 8
1 0001 1110 -2 1001 9
2 0010 1101 -3 1010 10
3 0011 1100 -4 1011 11
4 0100 1011 -5 1100 12
5 0101 1010 -6 1101 13
6 0110 1001 -7 1110 14
7 0111 1000 -8 1111 15
第一列是数字。下一列显示二进制补码表示。 "Inverted" 列显示反转位的二进制结果,下一列显示表示的十进制值。这是我们放置在结果高位的值。如您所见,数字已经交换位置,最低 (-8) 变为最高。这将为我们提供降序排序的值。
除非高位相等,否则低位无关紧要。 "Bit Flipped" 列显示了翻转高位后值的二进制表示。这样做是将数字 -8 到 7 映射到无符号范围 0 到 15,从而确保正确比较数字的低位。