My task is:
A sequence of non-empty strings stringList is given, containing only uppercase letters of the
Latin alphabet.
For all strings starting with the same letter, determine their total length and obtain a sequence
of strings of the form "S-C", where S is the total length of all strings from stringList that begin
with the character C. Order the resulting sequence in descending order of the numerical values
of the sums, and for equal values of the sums, in ascending order of the C character codes.
Everything needs to be done in one line via linq.
I tried this:
return stringList.GroupBy(x => x.FirstOrDefault())
.Select(x => x.ToString().Length + "-" + x.Key)
.OrderByDescending(g => g.Length)
.ThenBy(g => g.FirstOrDefault());
But this one does not work properly.
The following method will take a list of strings and returns the results as you have asked for:
IEnumerable<string> ProcessValues(IEnumerable<string> strings) {
return strings.GroupBy(s => s.FirstOrDefault())
.Select(group => (Character: group.Key, Length: group.Sum(s => s.Length)))
.OrderByDescending(g => g.Length)
.ThenBy(g => g.Character)
.Select(g => $"{g.Length}-{g.Character}");
}
An example of the results you can expect is provided below:
var stringList = new List<string> { "Hello", "How", "Are", "You", "Today" }
var result = ProcessValues(stringList); // { "8-H", "5-T", "3-A", "3-Y" }
Related
A sequence of non-empty strings stringList is given, containing only uppercase letters of the Latin alphabet. For all strings starting with the same letter, determine their total length and obtain a sequence of strings of the form "S-C", where S is the total length of all strings from stringList that begin with the character C.
var stringList = new[] { "YELLOW", "GREEN", "YIELD" };
var expected = new[] { "11-Y", "5-G" };
I tried this:
var groups =
from word in stringList
orderby word ascending
group word by word[0] into groupedByFirstLetter
orderby groupedByFirstLetter.Key descending
select new { key = groupedByFirstLetter.Key, Words = groupedByFirstLetter.Select(x => x.Length) };
But the output of this query is Y 6 5 G 5 instead of Y-11 G-5.
What I would like to know is how to sum the lengths if there is more than 1 word in the group, and how to format the result/display it as expected?
This should do it:
var results = stringList.OrderByDescending(x => x[0])
.ThenBy(x => x)
.GroupBy(x => x[0])
.Select(g => $"{g.Sum(x => x.Length)}-{g.Key}")
.ToArray();
var result = stringList.GroupBy(e => e[0]).Select(e => $"{e.Sum(o => o.Length)}-{e.Key}").ToArray();
Not sure I am able to rewrite it in your form.
I am working on project which is asp.net mvc core. I want to replace string list of duplicate values to one with comma separated,
List<string> stringList = surveylist.Split('&').ToList();
I have string list
This generate following output:
7=55
6=33
5=MCC
4=GHI
3=ABC
1003=DEF
1003=ABC
1=JKL
And I want to change output like this
7=55
6=33
5=MCC
4=GHI
3=ABC
1003=DEF,ABC
1=JKL
Duplicate items values should be comma separated.
There are probably 20 ways to do this. One simple one would be:
List<string> newStringList = stringList
.Select(a => new { KeyValue = a.Split("=") })
.GroupBy(a => a.KeyValue[0])
.Select(a => $"{a.Select(x => x.KeyValue[0]).First()}={string.Join(",", a.Select(x => x.KeyValue[1]))}")
.ToList();
Take a look at your output. Notice that an equal sign separates each string into a key-value pair. Think about how you want to approach this problem. Is a list of strings really the structure you want to build on? You could take a different approach and use a list of KeyValuePairs or a Dictionary instead.
If you really need to do it with a List, then look at the methods LINQ's Enumerable has to offer. Namely Select and GroupBy.
You can use Select to split once more on the equal sign: .Select(s => s.Split('=')).
You can use GroupBy to group values by a key: .GroupBy(pair => pair[0]).
To join it back to a string, you can use a Select again.
An end result could look something like this:
List<string> stringList = values.Split('&')
.Select(s => {
string[] pair = s.Split('=');
return new { Key = pair[0], Value = pair[1] };
})
.GroupBy(pair => pair.Key)
.Select(g => string.Concat(
g.Key,
'=',
string.Join(
", ",
g.Select(pair => pair.Value)
)
))
.ToList();
The group contains pairs so you need to select the value of each pair and join them into a string.
Is there a way using LINQ, to find if string from one array of strings contains (partial) string from another array of strings? Something like this:
string[] fullStrings = { "full_xxx_part_name", "full_ccc_part_name", "full_zzz_part_name" };
string[] stringParts = { "a_part", "b_part", "c_part", "e_part" };
// compare fullStrings array with stringParts array
// full_ccc_part_name contains c_part (first match is OK, no need to find all)
// return index 1 (index 1 from fullStrings array)
This is asked rather for educational purpose.
I'm aware that Linq does not magically avoid the loop, instead does it in the background.
You can use Where + Any with string methods:
string[] matches = fullStrings
.Where(s => stringParts.Any(s.Contains))
.ToArray();
If you want to compare in a case insensitive way use IndexOf:
string[] matches = fullStrings
.Where(s => stringParts.Any(part => s.IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0))
.ToArray();
In case you want the indexes:
int[] matches = fullStrings
.Select((s, index) => (String: s, Index: index))
.Where(x => stringParts.Any(x.String.Contains))
.Select(x => x.Index)
.ToArray();
You would of course need to use some type of loop to find the index. Here is a solution using Linq.
This will return the first index if a match is found or -1 if none is found:
var index = fullStrings
.Select((s,i) => (s, i))
.Where(x => stringParts.Any(x.s.Contains))
.Select(x => x.i)
.DefaultIfEmpty(-1)
.First();
I was looking into how to check character duplicates and I came across this method, it works, but I am trying to understand how it works. If anyone could explain this method so I can better understand what is occurring I would greatly appreciate it. Thank you.
static int duplicateAmount(string word)
{
var duplicates = word.GroupBy(a => a)
.Where(g => g.Count() > 1)
.Select(i => new { Number = i.Key, Count = i.Count() });
return duplicates.Count();
}
The idea is to group the characters in the string and check if any group contains more than one elements, signifying duplicate occurrence of characters. For example,
word.GroupBy would produce a grouping result as the following.
As you can observe, the characters t,i,and s has more than one occurrences. The Where condition filters the groups which has more than one element and the count method counts the numbers of filtered groups.
In your case, if you are interested only in counting the number of characters that are duplicate, you could refactor the method further as
static int duplicateAmount(string word)
{
return word.GroupBy(a => a)
.Count(g => g.Count() > 1);
}
This avoids creation of intermediate types, which is not quite required if you are interested only the count
When you iterate a string, you do so by iterating all its characters.
Therefore:
static int duplicateAmount(string word)
{
var duplicates = word.GroupBy(a => a) // Groups all the unique chars
.Where(g => g.Count() > 1) // filters the groups with more than one entry
// Maps the query result to an anonymous object containing the char
// and their amount of occurrences
.Select(i => new { Number = i.Key, Count = i.Count() });
// return the count of elements in the resulting collection
return duplicates.Count();
}
Now that you have understood that, you can probably tell the last step (the mapping) is unnecessary since we're creating a structure we're not using at all: { Number, Count}.
The code can perfectly be
static int duplicateAmount(string word)
{
return word.GroupBy(a => a) // Groups all the unique chars
// Counts the amount of groups with more than one occurrence.
.Count(g => g.Count() > 1);
}
Edited: Removed the where clause as noted in the comments. Thanks #DrkDeveloper
I will try to describe my question in the best way I can.
I have a list with X strings ("NOTION", "CATION", "COIN", "NOON").
I am trying to compare them and find the most times each character (letter) was used, use that to get the number of that character, arrange them in alphabetical order, and create a string.
So the result string should be: "ACINNOOT"
Hope is clear what I am describing.
EDIT
So far:
for (int i = 0; i < currentWord.Length; i++)
{
string letter = word.Substring(i, 1);
tempDuplicatedLetterList.Add(letter);
}
// Which letters are repeated and how many times
var duplicatedQuery = tempDuplicatedLetterList.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { Element = y.Key, Counter = y.Count() })
.ToList();
I came to this, although I think there might be a cleaner way to do it:
var characterSets = new string[] { "NOTION", "CATION", "COIN", "NOON" }
.SelectMany(c => c.GroupBy(cc => cc)) // create character groups for each string, and flatten the groups
.GroupBy(c => c.Key) // group the groups
.OrderBy(cg => cg.Key) // order by the character (alphabetical)
.Select(cg => new string(cg.Key, cg.Max(v => v.Count()))) // create a string for each group, using the maximum count for that character
.ToArray(); // make an array
var result = string.Concat(characterSets);