如何使用星号制作垂直直方图?
How do i make a vertical histogram using asterisks?
我正在尝试解决名为 Simple Fun #358: Vertical Histogram Of Letters : https://www.codewars.com/kata/59cf0ba5d751dffef300001f/javascript
的 codewars 问题
总而言之,他们给出了一个字符串输入,所以我必须搜索可能的大写字母 [A-Z],所以如果我找到一个,那么我必须计算它在字符串中出现的次数。之后,我必须使用 asteriks 制作一个垂直直方图,我找到的每个字母都有一个垂直条,高度或长度基于字母出现的时间。
示例:AAABBC
*
* *
* * *
A B C
但是当我试图解决这个问题时,我得到了这个:
*
*
*
A *
*
*
B *
C
这是我尝试过的:
public static string VerticalHistogramOf(string s)
{
string alphabet = "ABCDEFGHIJKLMNOPQRSTUWXYZ";
var dic = new Dictionary<char, int>();
string histogram = "";
foreach (var a in alphabet)
{
if (s.Contains(a))
dic.Add(a, s.Count(x => x == a));
}
foreach (var item in dic)
{
string bar = "";
for (int i = 0; i < item.Value; i++)
{
bar += "*\n";
}
bar += item.Key + " ";
histogram += bar;
}
return histogram;
}
那么制作这样的直方图的算法是什么?我想用最简单和可读的方式
这很有趣...我想我会使用 LINQy 方法尝试一下:
static void Main(string[] args)
{
string s = "XXY YY ZZZ123ZZZ AAA BB C";
string histogram = VerticalHistogram(s);
Console.Write(histogram);
Console.Write("Press Enter to Quit.");
Console.ReadLine();
}
private static string VerticalHistogram(string s)
{
StringBuilder sb = new StringBuilder();
// this builds a "dictionary" like you did, but using LINQ
// it first only lets upper case letters pass through
// then it groups by those letters and stores the letter with its count
// then it sorts by the letter
var letterCounts =
s.Where(c => Char.IsUpper(c))
.GroupBy(c => c, (key, element) => new
{
Letter = key,
Count = element.Count()
})
.OrderBy(g => g.Letter);
// this gets the maximum count of all the letters,
// which is the max height of our histogram
var maxHeight = letterCounts.Max(g => g.Count);
// this generates the numbers from 1 to maxHeight,
// then reverses the list.
// next we take each height value going from largest to smallest,
// and we ask each letter count if it is larger than that value or not.
// this generates an array of "*" or " " based on the current height
// and each letter count
// we put that array together with String.Join() to make up the row
// for the current height and add it to our StringBuilder
Enumerable.Range(1, maxHeight).Reverse().ToList().ForEach(h =>
sb.AppendLine(String.Join(" ", letterCounts.Select(g => g.Count >= h ? "*" : " ")))
);
// this creates a row with all the letters for the bottom of the histogram
sb.AppendLine(String.Join(" ", letterCounts.Select(g => g.Letter)));
return sb.ToString();
}
生成以下输出:
*
*
*
* * *
* * * * *
* * * * * *
A B C X Y Z
Press Enter to Quit.
作为一种变体,您可以在 Enumerable.Range() 调用中制作整个直方图,如下所示:
Enumerable.Range(0, maxHeight).Reverse().ToList().ForEach(h =>
sb.AppendLine(String.Join(" ", letterCounts.Select(g =>
(h==0) ? g.Letter.ToString() :
(g.Count >= h) ? "*" : " ")))
);
那你就不用单独把字母加到最后一行了。
这是一个四线:
var data = "AAABBC";
var lookup = data.ToLookup(x => x);
var height = lookup.Max(x => x.Count());
var histogram =
String.Join(
Environment.NewLine,
Enumerable
.Range(0, height)
.Reverse()
.Select(x =>
String.Concat(
lookup
.SelectMany(y =>
y
.Select(z => "*")
.Concat(Enumerable.Repeat(" ", height - 1))
.Skip(x)
.Take(1))))
.Concat(new[] { String.Concat(lookup.Select(x => x.Key)) }));
这给了我:
*
**
***
ABC
使用 data = "AAABBCCCCC";
我得到:
*
*
* *
***
***
ABC
这是另一种不是“LINQy”的方法,更类似于您正在做的事情:
private string VerticalHistogram_non_LINQy(string s)
{
SortedDictionary<char, int> letterCounts = new SortedDictionary<char, int>();
foreach(char c in s)
{
if(Char.IsUpper(c))
{
if (!letterCounts.ContainsKey(c))
{
letterCounts.Add(c, 1);
}
else
{
letterCounts[c]++;
}
}
}
int? maxHeight = null;
foreach (KeyValuePair<char, int> kvp in letterCounts)
{
if (!maxHeight.HasValue)
{
maxHeight = kvp.Value;
}
else if (kvp.Value > maxHeight)
{
maxHeight = kvp.Value;
}
}
StringBuilder sb = new StringBuilder();
if (maxHeight.HasValue)
{
for (int h=maxHeight.Value; h>0; h--)
{
StringBuilder row = new StringBuilder();
foreach (KeyValuePair<char, int> kvp in letterCounts)
{
row.Append((row.Length>0 ? " " : "") + (kvp.Value>=h ? "*" : " "));
}
sb.AppendLine(row.ToString());
}
sb.AppendLine(String.Join(" ", letterCounts.Keys));
}
return sb.ToString();
}
我正在尝试解决名为 Simple Fun #358: Vertical Histogram Of Letters : https://www.codewars.com/kata/59cf0ba5d751dffef300001f/javascript
的 codewars 问题总而言之,他们给出了一个字符串输入,所以我必须搜索可能的大写字母 [A-Z],所以如果我找到一个,那么我必须计算它在字符串中出现的次数。之后,我必须使用 asteriks 制作一个垂直直方图,我找到的每个字母都有一个垂直条,高度或长度基于字母出现的时间。
示例:AAABBC
*
* *
* * *
A B C
但是当我试图解决这个问题时,我得到了这个:
*
*
*
A *
*
*
B *
C
这是我尝试过的:
public static string VerticalHistogramOf(string s)
{
string alphabet = "ABCDEFGHIJKLMNOPQRSTUWXYZ";
var dic = new Dictionary<char, int>();
string histogram = "";
foreach (var a in alphabet)
{
if (s.Contains(a))
dic.Add(a, s.Count(x => x == a));
}
foreach (var item in dic)
{
string bar = "";
for (int i = 0; i < item.Value; i++)
{
bar += "*\n";
}
bar += item.Key + " ";
histogram += bar;
}
return histogram;
}
那么制作这样的直方图的算法是什么?我想用最简单和可读的方式
这很有趣...我想我会使用 LINQy 方法尝试一下:
static void Main(string[] args)
{
string s = "XXY YY ZZZ123ZZZ AAA BB C";
string histogram = VerticalHistogram(s);
Console.Write(histogram);
Console.Write("Press Enter to Quit.");
Console.ReadLine();
}
private static string VerticalHistogram(string s)
{
StringBuilder sb = new StringBuilder();
// this builds a "dictionary" like you did, but using LINQ
// it first only lets upper case letters pass through
// then it groups by those letters and stores the letter with its count
// then it sorts by the letter
var letterCounts =
s.Where(c => Char.IsUpper(c))
.GroupBy(c => c, (key, element) => new
{
Letter = key,
Count = element.Count()
})
.OrderBy(g => g.Letter);
// this gets the maximum count of all the letters,
// which is the max height of our histogram
var maxHeight = letterCounts.Max(g => g.Count);
// this generates the numbers from 1 to maxHeight,
// then reverses the list.
// next we take each height value going from largest to smallest,
// and we ask each letter count if it is larger than that value or not.
// this generates an array of "*" or " " based on the current height
// and each letter count
// we put that array together with String.Join() to make up the row
// for the current height and add it to our StringBuilder
Enumerable.Range(1, maxHeight).Reverse().ToList().ForEach(h =>
sb.AppendLine(String.Join(" ", letterCounts.Select(g => g.Count >= h ? "*" : " ")))
);
// this creates a row with all the letters for the bottom of the histogram
sb.AppendLine(String.Join(" ", letterCounts.Select(g => g.Letter)));
return sb.ToString();
}
生成以下输出:
*
*
*
* * *
* * * * *
* * * * * *
A B C X Y Z
Press Enter to Quit.
作为一种变体,您可以在 Enumerable.Range() 调用中制作整个直方图,如下所示:
Enumerable.Range(0, maxHeight).Reverse().ToList().ForEach(h =>
sb.AppendLine(String.Join(" ", letterCounts.Select(g =>
(h==0) ? g.Letter.ToString() :
(g.Count >= h) ? "*" : " ")))
);
那你就不用单独把字母加到最后一行了。
这是一个四线:
var data = "AAABBC";
var lookup = data.ToLookup(x => x);
var height = lookup.Max(x => x.Count());
var histogram =
String.Join(
Environment.NewLine,
Enumerable
.Range(0, height)
.Reverse()
.Select(x =>
String.Concat(
lookup
.SelectMany(y =>
y
.Select(z => "*")
.Concat(Enumerable.Repeat(" ", height - 1))
.Skip(x)
.Take(1))))
.Concat(new[] { String.Concat(lookup.Select(x => x.Key)) }));
这给了我:
* ** *** ABC
使用 data = "AAABBCCCCC";
我得到:
* * * * *** *** ABC
这是另一种不是“LINQy”的方法,更类似于您正在做的事情:
private string VerticalHistogram_non_LINQy(string s)
{
SortedDictionary<char, int> letterCounts = new SortedDictionary<char, int>();
foreach(char c in s)
{
if(Char.IsUpper(c))
{
if (!letterCounts.ContainsKey(c))
{
letterCounts.Add(c, 1);
}
else
{
letterCounts[c]++;
}
}
}
int? maxHeight = null;
foreach (KeyValuePair<char, int> kvp in letterCounts)
{
if (!maxHeight.HasValue)
{
maxHeight = kvp.Value;
}
else if (kvp.Value > maxHeight)
{
maxHeight = kvp.Value;
}
}
StringBuilder sb = new StringBuilder();
if (maxHeight.HasValue)
{
for (int h=maxHeight.Value; h>0; h--)
{
StringBuilder row = new StringBuilder();
foreach (KeyValuePair<char, int> kvp in letterCounts)
{
row.Append((row.Length>0 ? " " : "") + (kvp.Value>=h ? "*" : " "));
}
sb.AppendLine(row.ToString());
}
sb.AppendLine(String.Join(" ", letterCounts.Keys));
}
return sb.ToString();
}