我的意思是:
给定输入的数字集:
1,2,3,4,5变为“1-5”。
<1,2> 1,2,7,9,10,11,12,14变为“1-3,5,7,9-12,14”这是我设法提出的最好的:[C#]
对我来说这感觉有点草率,所以问题是,是否有某种更可读和/或更优雅的解决方案呢?
public static string[] FormatInts(int[] ints)
{
if (ints == null)
throw new ArgumentNullException("ints"); // hey what are you doing?
if (ints.Length == 0)
return new string[] { "" }; // nothing to process
if (ints.Length == 1)
return new string[] { ints[0].ToString() }; // nothing to process
Array.Sort<int>(ints); // need to sort these lil' babies
List<string> values = new List<string>();
int lastNumber = ints[0]; // start with the first number
int firstNumber = ints[0]; // same as above
for (int i = 1; i < ints.Length; i++)
{
int current = ints[i];
int difference = (lastNumber - current ); // compute difference between last number and current number
if (difference == -1) // the numbers are adjacent
{
if (firstNumber == 0) // this is the first of the adjacent numbers
{
firstNumber = lastNumber;
}
else // we're somehow in the middle or at the end of the adjacent number set
{
lastNumber = current;
continue;
}
}
else
{
if (firstNumber > 0 && firstNumber != lastNumber) // get ready to print a set of numbers
{
values.Add(string.Format("{0}-{1}", firstNumber, lastNumber));
firstNumber = 0; // reset
}
else // print a single value
{
values.Add(string.Format("{0}", lastNumber));
}
}
lastNumber = current;
}
if (firstNumber > 0) // if theres anything left, print it out
{
values.Add(string.Format("{0}-{1}", firstNumber, lastNumber));
}
return values.ToArray();
}
答案 0 :(得分:10)
我已经重写了你的代码:
public static string[] FormatInts(int[] ints)
{
Array.Sort<int>(ints);
List<string> values = new List<string>();
for (int i = 0; i < ints.Length; i++)
{
int groupStart = ints[i];
int groupEnd = groupStart;
while (i < ints.Length - 1 && ints[i] - ints[i + 1] == -1)
{
groupEnd = ints[i + 1];
i++;
}
values.Add(string.Format(groupEnd == groupStart ? "{0}":"{0} - {1}", groupStart, groupEnd));
}
return values.ToArray();
}
然后:
/////////////////
int[] myInts = { 1,2,3,5,7,9,10,11,12,14 };
string[] result = FormatInts(myInts); // now result haves "1-3", "5", "7", "9-12", "14"
答案 1 :(得分:3)
纯功能Python:
#!/bin/env python
def group(nums):
def collect((acc, i_s, i_e), n):
if n == i_e + 1: return acc, i_s, n
return acc + ["%d"%i_s + ("-%d"%i_e)*(i_s!=i_e)], n, n
s = sorted(nums)+[None]
acc, _, __ = reduce(collect, s[1:], ([], s[0], s[0]))
return ", ".join(acc)
assert group([1,2,3,5,7,9,10,11,12,14]) == "1-3, 5, 7, 9-12, 14"
答案 2 :(得分:3)
请参阅How would you display an array of integers as a set of ranges? (algorithm)
My answer上述问题:
void ranges(int n; int a[n], int n)
{
qsort(a, n, sizeof(*a), intcmp);
for (int i = 0; i < n; ++i) {
const int start = i;
while(i < n-1 and a[i] >= a[i+1]-1)
++i;
printf("%d", a[start]);
if (a[start] != a[i])
printf("-%d", a[i]);
if (i < n-1)
printf(",");
}
printf("\n");
}
答案 3 :(得分:3)
我参加派对有点晚了,但无论如何,这是我使用Linq的版本:
public static string[] FormatInts(IEnumerable<int> ints)
{
var intGroups = ints
.OrderBy(i => i)
.Aggregate(new List<List<int>>(), (acc, i) =>
{
if (acc.Count > 0 && acc.Last().Last() == i - 1) acc.Last().Add(i);
else acc.Add(new List<int> { i });
return acc;
});
return intGroups
.Select(g => g.First().ToString() + (g.Count == 1 ? "" : "-" + g.Last().ToString()))
.ToArray();
}
答案 4 :(得分:1)
对我来说看起来很清楚直白。如果您假设输入数组已排序,或者在进一步处理之前自行排序,则可以简化一点。
我建议的唯一调整是反转减法:
int difference = (current - lastNumber);
...仅仅因为我发现更容易处理积极的差异。但是您的代码很高兴阅读!
答案 5 :(得分:1)
使用输入验证/预排序
如果你需要做一些更加花哨的事情,你可以轻松地将结果作为一个LoL 只需返回一个字符串。
#!/usr/bin/perl -w
use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
sub adjacenify {
my @input = @_;
# Validate and sort
looks_like_number $_ or
die "Saw '$_' which doesn't look like a number" for @input;
@input = sort { $a <=> $b } @input;
my (@output, @range);
@range = (shift @input);
for (@input) {
if ($_ - $range[-1] <= 1) {
push @range, $_ unless $range[-1] == $_; # Prevent repetition
}
else {
push @output, [ @range ];
@range = ($_);
}
}
push @output, [ @range ] if @range;
# Return the result as a string. If a sequence is size 1, then it's just that number.
# Otherwise, it's the first and last number joined by '-'
return join ', ', map { 1 == @$_ ? @$_ : join ' - ', $_->[0], $_->[-1] } @output;
}
print adjacenify( qw/1 2 3 5 7 9 10 11 12 14/ ), "\n";
print adjacenify( 1 .. 5 ), "\n";
print adjacenify( qw/-10 -9 -8 -1 0 1 2 3 5 7 9 10 11 12 14/ ), "\n";
print adjacenify( qw/1 2 4 5 6 7 100 101/), "\n";
print adjacenify( qw/1 62/), "\n";
print adjacenify( qw/1/), "\n";
print adjacenify( qw/1 2/), "\n";
print adjacenify( qw/1 62 63/), "\n";
print adjacenify( qw/-2 0 0 2/), "\n";
print adjacenify( qw/-2 0 0 1/), "\n";
print adjacenify( qw/-2 0 0 1 2/), "\n";
输出:
1 - 3, 5, 7, 9 - 12, 14
1 - 5
-10 - -8, -1 - 3, 5, 7, 9 - 12, 14
1 - 2, 4 - 7, 100 - 101
1, 62
1
1 - 2
1, 62 - 63
-2, 0, 2
-2, 0 - 1
-2, 0 - 2
-2, 0 - 2
一个很好的递归解决方案:
sub _recursive_adjacenify($$);
sub _recursive_adjacenify($$) {
my ($input, $range) = @_;
return $range if ! @$input;
my $number = shift @$input;
if ($number - $range->[-1] <= 1) {
return _recursive_adjacenify $input, [ @$range, $number ];
}
else {
return $range, _recursive_adjacenify $input, [ $number ];
}
}
sub recursive_adjacenify {
my @input = @_;
# Validate and sort
looks_like_number $_ or
die "Saw '$_' which doesn't look like a number" for @input;
@input = sort { $a <=> $b } @input;
my @output = _recursive_adjacenify \@input, [ shift @input ];
# Return the result as a string. If a sequence is size 1,
# then it's just that number.
# Otherwise, it's the first and last number joined by '-'
return join ', ', map { 2 == @$_ && $_->[0] == $_->[1] ? $_->[0] :
1 == @$_ ? @$_ :
join ' - ', $_->[0], $_->[-1] } @output;
}
答案 6 :(得分:1)
正如我在评论中写的那样,我并不喜欢使用值0作为标志,使firstNumber成为一个值和一个标志。
我用Java快速实现了算法,大胆地跳过了你已经正确覆盖的有效性测试......
public class IntListToRanges
{
// Assumes all numbers are above 0
public static String[] MakeRanges(int[] numbers)
{
ArrayList<String> ranges = new ArrayList<String>();
Arrays.sort(numbers);
int rangeStart = 0;
boolean bInRange = false;
for (int i = 1; i <= numbers.length; i++)
{
if (i < numbers.length && numbers[i] - numbers[i - 1] == 1)
{
if (!bInRange)
{
rangeStart = numbers[i - 1];
bInRange = true;
}
}
else
{
if (bInRange)
{
ranges.add(rangeStart + "-" + numbers[i - 1]);
bInRange = false;
}
else
{
ranges.add(String.valueOf(numbers[i - 1]));
}
}
}
return ranges.toArray(new String[ranges.size()]);
}
public static void ShowRanges(String[] ranges)
{
for (String range : ranges)
{
System.out.print(range + ","); // Inelegant but quickly coded...
}
System.out.println();
}
/**
* @param args
*/
public static void main(String[] args)
{
int[] an1 = { 1,2,3,5,7,9,10,11,12,14,15,16,22,23,27 };
int[] an2 = { 1,2 };
int[] an3 = { 1,3,5,7,8,9,11,12,13,14,15 };
ShowRanges(MakeRanges(an1));
ShowRanges(MakeRanges(an2));
ShowRanges(MakeRanges(an3));
int L = 100;
int[] anr = new int[L];
for (int i = 0, c = 1; i < L; i++)
{
int incr = Math.random() > 0.2 ? 1 : (int) Math.random() * 3 + 2;
c += incr;
anr[i] = c;
}
ShowRanges(MakeRanges(anr));
}
}
我不会说它比你的算法更优雅/更有效,当然......只是不同的东西。
请注意,1,5,6,9可以写成1,5-6,9或1,5,6,9,不确定哪个更好(如果有的话)。
我记得做过类似的事情(在C中),将消息号分组到Imap范围,因为它更有效。一个有用的算法。
答案 7 :(得分:1)
短而甜的红宝石
def range_to_s(range)
return range.first.to_s if range.size == 1
return range.first.to_s + "-" + range.last.to_s
end
def format_ints(ints)
range = []
0.upto(ints.size-1) do |i|
range << ints[i]
unless (range.first..range.last).to_a == range
return range_to_s(range[0,range.length-1]) + "," + format_ints(ints[i,ints.length-1])
end
end
range_to_s(range)
end
答案 8 :(得分:1)
我的第一个想法,在Python中:
def seq_to_ranges(seq):
first, last = None, None
for x in sorted(seq):
if last != None and last + 1 != x:
yield (first, last)
first = x
if first == None: first = x
last = x
if last != None: yield (first, last)
def seq_to_ranges_str(seq):
return ", ".join("%d-%d" % (first, last) if first != last else str(first) for (first, last) in seq_to_ranges(seq))
可能更干净,但它仍然很容易。
对Haskell的简单翻译:
import Data.List
seq_to_ranges :: (Enum a, Ord a) => [a] -> [(a, a)]
seq_to_ranges = merge . foldl accum (id, Nothing) . sort where
accum (k, Nothing) x = (k, Just (x, x))
accum (k, Just (a, b)) x | succ b == x = (k, Just (a, x))
| otherwise = (k . ((a, b):), Just (x, x))
merge (k, m) = k $ maybe [] (:[]) m
seq_to_ranges_str :: (Enum a, Ord a, Show a) => [a] -> String
seq_to_ranges_str = drop 2 . concatMap r2s . seq_to_ranges where
r2s (a, b) | a /= b = ", " ++ show a ++ "-" ++ show b
| otherwise = ", " ++ show a
大致相同。
答案 9 :(得分:1)
交互式J会话的脚本(用户输入缩进3个空格,ASCII框中的文本为J输出):
g =: 3 : '<@~."1((y~:1+({.,}:)y)#y),.(y~:(}.y,{:y)-1)#y'@/:~"1
g 1 2 3 4 5
+---+
|1 5|
+---+
g 1 2 3 5 7 9 10 11 12 14
+---+-+-+----+--+
|1 3|5|7|9 12|14|
+---+-+-+----+--+
g 12 2 14 9 1 3 10 5 11 7
+---+-+-+----+--+
|1 3|5|7|9 12|14|
+---+-+-+----+--+
g2 =: 4 : '<(>x),'' '',>y'/@:>@:(4 :'<(>x),''-'',>y'/&.>)@((<@":)"0&.>@g)
g2 12 2 14 9 1 3 10 5 11 7
+---------------+
|1-3 5 7 9-12 14|
+---------------+
(;g2) 5 1 20 $ (i.100) /: ? 100 $ 100
+-----------------------------------------------------------+
|20 39 82 33 72 93 15 30 85 24 97 60 87 44 77 29 58 69 78 43|
| |
|67 89 17 63 34 41 53 37 61 18 88 70 91 13 19 65 99 81 3 62|
| |
|31 32 6 11 23 94 16 73 76 7 0 75 98 27 66 28 50 9 22 38|
| |
|25 42 86 5 55 64 79 35 36 14 52 2 57 12 46 80 83 84 90 56|
| |
| 8 96 4 10 49 71 21 54 48 51 26 40 95 1 68 47 59 74 92 45|
+-----------------------------------------------------------+
|15 20 24 29-30 33 39 43-44 58 60 69 72 77-78 82 85 87 93 97|
+-----------------------------------------------------------+
|3 13 17-19 34 37 41 53 61-63 65 67 70 81 88-89 91 99 |
+-----------------------------------------------------------+
|0 6-7 9 11 16 22-23 27-28 31-32 38 50 66 73 75-76 94 98 |
+-----------------------------------------------------------+
|2 5 12 14 25 35-36 42 46 52 55-57 64 79-80 83-84 86 90 |
+-----------------------------------------------------------+
|1 4 8 10 21 26 40 45 47-49 51 54 59 68 71 74 92 95-96 |
+-----------------------------------------------------------+
可读和优雅在旁观者眼中:D
这是一个很好的练习!它建议了Perl的以下部分:
sub g {
my ($i, @r, @s) = 0, local @_ = sort {$a<=>$b} @_;
$_ && $_[$_-1]+1 == $_[$_] || push(@r, $_[$_]),
$_<$#_ && $_[$_+1]-1 == $_[$_] || push(@s, $_[$_]) for 0..$#_;
join ' ', map {$_ == $s[$i++] ? $_ : "$_-$s[$i-1]"} @r;
}
在简单的英语中,该算法查找前一项不少于一项的所有项目,将它们用于下限;找到下一个项目不是一个更大的所有项目,将它们用于上限;并将两个列表逐项组合在一起。
由于J非常模糊,所以这里有一个关于代码如何工作的简短说明:
x /: y
对x
上的数组y
进行排序。 ~
可以将一个二元动词变成一个自反的monad,所以/:~
意味着“对自己排序一个数组”。
3 : '...'
声明了一个monadic动词(J的方式是说“函数采用一个参数”)。 @
表示函数组合,因此g =: 3 : '...' @ /:~
表示“g
设置为我们正在定义的函数,但其参数首先排序”。 "1
表示我们对数组进行操作,而不是表格或更高维度的任何内容。
注意:y
始终是monadic动词唯一参数的名称。
{.
获取数组的第一个元素(head),}:
获取除最后一个元素(缩减)之外的所有元素。 ({.,}:)y
有效地复制y
的第一个元素,并删除最后一个元素。 1+({.,}:)y
为它添加1,~:
比较两个数组,无论它们在哪里都是真的,无论它们在哪里都是假的,所以y~:1+({.,}:)y
是一个在所有索引中都为真的数组y
,其中元素不等于前面的元素。 (y~:1+({.,}:)y)#y
选择y
中前一句中所述属性为真的所有元素。
类似地,}.
除了数组的第一个元素(behead)和{:
之外的所有元素都采用最后一个(尾部),因此}.y,{:y
除了y
之外的所有元素。 1}},最后一个元素重复。 (}.y,{:y)-1
将1减去1,~:
再次#
比较两个数组,以便在,.
选择时不相等。
~.
将两个数组一起压缩成一个双元素数组的数组。 "1
nubs一个列表(消除重复),并且被赋予@
等级,因此它在内部双元素数组而不是顶级数组上运行。这是由<
组成的g2
,它将每个子数组放入一个框中(否则J将再次扩展每个子数组以形成2D表)。
{{1}}是一堆拳击和拆箱(否则J将填充字符串等长),并且非常无趣。
答案 10 :(得分:1)
这是我的Haskell条目:
runs lst = map showRun $ runs' lst
runs' l = reverse $ map reverse $ foldl newOrGlue [[]] l
showRun [s] = show s
showRun lst = show (head lst) ++ "-" ++ (show $ last lst)
newOrGlue [[]] e = [[e]]
newOrGlue (curr:other) e | e == (1 + (head curr)) = ((e:curr):other)
newOrGlue (curr:other) e | otherwise = [e]:(curr:other)
和示例运行:
T> runs [1,2,3,5,7,9,10,11,12,14]
["1-3","5","7","9-12","14"]
答案 11 :(得分:1)
Erlang,在输入时也执行排序和唯一,并且可以生成可编程的可重用对以及字符串表示。
group(List) ->
[First|_] = USList = lists:usort(List),
getnext(USList, First, 0).
getnext([Head|Tail] = List, First, N) when First+N == Head ->
getnext(Tail, First, N+1);
getnext([Head|Tail] = List, First, N) ->
[ {First, First+N-1} | getnext(List, Head, 0) ];
getnext([], First, N) -> [{First, First+N-1}].
%%%%%% pretty printer
group_to_string({X,X}) -> integer_to_list(X);
group_to_string({X,Y}) -> integer_to_list(X) ++ "-" ++ integer_to_list(Y);
group_to_string(List) -> [group_to_string(X) || X <- group(List)].
测试以编程方式重用对:
壳&GT;测试:组([34,3415,56,58,57,11,12,13,1,2,3,3,4,5])
结果&GT; [{1,5},{11,13},{34,34},{56,58},{3415,3415}]
测试获得“漂亮”的字符串:
壳&GT;测试:group_to_string([34,3415,56,58,57,11,12,13,1,2,3,3,4,5])
结果&GT; [ “1-5”, “11-13”, “34”, “56-58”, “3415”]
希望它有所帮助 再见答案 12 :(得分:0)
VBA
Public Function convertListToRange(lst As String) As String
Dim splLst() As String
splLst = Split(lst, ",")
Dim x As Long
For x = 0 To UBound(splLst)
Dim groupStart As Integer
groupStart = splLst(x)
Dim groupEnd As Integer
groupEnd = groupStart
Do While (x <= UBound(splLst) - 1)
If splLst(x) - splLst(x + 1) <> -1 Then Exit Do
groupEnd = splLst(x + 1)
x = x + 1
Loop
convertListToRange = convertListToRange & IIf(groupStart = groupEnd, groupStart & ",", groupStart & "-" & groupEnd & ",")
Next x
convertListToRange = Left(convertListToRange, Len(convertListToRange) - 1)
End Function
convertListToRange(&#34; 1,2,3,7,8,9,11,12,99,100,101&#34)
返回:&#34; 1-3,7-9,11-12,99-101&#34;