Iterate over a collection of strings using LINQ - c#

I put the following code segment in .NET Fiddle but it printed out System.Linq.Enumerable+WhereArrayIterator1[System.String] I'd like to print out each content in result, in order to understand how Select works. Can someone please help to point out what the problem is? Many thanks!
string[] sequ1 = { "abcde", "fghi", "jkl", "mnop", "qrs" };
string[] sequ2 = { "abc", "defgh", "ijklm", "nop" };
var result =sequ1.Select( n1 => sequ2.Where(n2 => n1.Length < n2.Length) );
foreach( var y in result)
{
Console.WriteLine(y);
}

You are actually returning a collection of collections.
sequ1.Select( n1 => sequ2.Where(n2 => n1.Length < n2.Length) );
For each element in sequ1, this statement filters sequ2 to find all of the elements from the second sequence where the current value in the first sequence is shorter than it and then maps to a new collection containing each of those results.
To describe what Select is actually doing:
You start with a collection of things. In your case: sequ1 which has type IEnumerable<string>
You supply it with a function, this function takes an argument of the type of thing you supplied it with a collection of and has a return type of some other thing, in your case:
fun n1 => sequ2.Where(n2 => n1.Length < n2.Length)
Your function takes a string and returns an IEnumerable<string>
Finally, it returns a result containing a collection of each element in the original collection transformed to some new element by the function you supplied it with.
So you started with IEnumerable<string> and ended up with IEnumerable<IEnumerable<string>>.
That means you have a collection for each value that appears in sequ1.
As such, you would expect the result to be:
{{}, {"defgh", "ijklm"}, {"defgh", "ijklm"}, {"defgh", "ijklm"}, {"defgh", "ijklm"}}
You can inspect the results by adding another loop.
foreach(var y in result)
{
foreach(var z in result)
{
Console.WriteLine(z);
}
}

Change your Select to SelectMany:
var result = sequ1.SelectMany(n1 => sequ2.Where(n2 => n1.Length < n2.Length));

I may be wrong, but I think the OP wants to compare both arrays, and for each element, print the longest one.
If that's the case, I would do it as follows:
var result = sequ1.Take(sequ2.Length)
.Select((n1, i) =>
(n1.Length > sequ2.ElementAt(i).Length)
? n1
: sequ2.ElementAt(i));
Explanation:
Use Take to only go as long as the length of the second array, and avoid nullreference exceptions later on.
Use Select, with two arguments, the first is the string, the second is the index.
Use ElementAt to find the corresponding element in sequ2

I don't know about this example is about to help you to understand how select work. A more simple exmaple what i think is this.
public class Person {
public string Name { get; set; }
public string LastName { get; set; }
}
public class Test {
public Test() {
List<Person> persons = new List<Person>();
persons.Add(new Person() { Name = "Person1",LastName = "LastName1" });
persons.Add(new Person() { Name = "Person2",LastName = "LastName2" });
var getNamesFromPersons = persons.Select(p => p.Name);
}
}

If you are beginning c#, you need to sideline the keyword "var" from your code.
Force yourself to write out what the variables really are:
If you forego the use of var, you would have seen why your code was Console.Writing what it did.
string[] sequ1 = { "abcde", "fghi", "jkl", "mnop", "qrs", };
string[] sequ2 = { "abc", "defgh", "ijklm", "nop", };
IEnumerable<IEnumerable<string>> result = sequ1.Select(n1 => sequ2.Where(n2 => n1.Length < n2.Length));
foreach (IEnumerable<string> y in result)
{
foreach (string z in y)
{
Console.WriteLine(z);
}
}

Related

Order IGrouping in C#

Example is here, should work in online compilers:
internal class Program
{
static void Main(string[] args)
{
var i1 = new Item();
i1.Val1 = 1;
i1.Val2 = 2.1;
var i2 = new Item();
i2.Val1 = 1;
i2.Val2 = 1.5;
var i3 = new Item();
i3.Val1 = 3;
i3.Val2 = 0.3;
var list = new List<Item>
{
i1,
i2,
i3
};
var grouped = list.GroupBy(x => x.Val1);
Program p = new Program();
foreach(var group in grouped)
p.Func(group);
}
public void Func(IGrouping<int, Item> list)
{
list.OrderBy(x => x.Val2); //list will be ordered, but not saved
list = (IGrouping<int, Item>)list.OrderBy(x => x.Val2); //exception
}
}
public class Item
{
public int Val1 { get; set; }
public double Val2 { get; set; }
}
It's simplified code of what I'm trying to do - I need to order list inside Func, but I have no idea how. First line works in theory, but since it's not a void it's not working in practice - list is not actually ordered.
Second line should work, actually Visual Studio suggested that, but it throws runtime exception - Unable to cast object of type System.Linq.OrderedEnumerable to System.Linq.IGrouping.
I'm out of ideas for the time being, but there is no way of bypassing it - I absolutely need to order it there.
Edit
My current solution is to use Select(x => x) to flatten the IGrouping to normal List, this way I can easily order it and edit values without losing reference to grouped. If you really want to keep IGrouping then you are out of luck, does not seem to be possible.
Try this.
var grouped = list.GroupBy(x => x.Val1).Select(a=> a.OrderBy(a=>a.Val2).ToList());
OrderBy returns IOrderedEnumerable you can't cast that to IGrouping
Use First method at the end in order to get IGrouping collection of ordered items.
public void Func(IGrouping<int, Item> list)
{
list = list.OrderBy(x => x.Val2).GroupBy(x => x.Val1).First();
}
Your example code doesn't show what you are trying to arrive at.
list.OrderBy(x => x.Val2); //list will be ordered, but not saved
OrderBy doesn't order the existing collection in-place. It effectively returns a new collection.
list = (IGrouping<int, Item>)list.OrderBy(x => x.Val2); //exception
OrderBy returns an IOrderedEnumerable<TElement>. Both IOrderedEnumerable<TElement> and IGrouping<TKey,TElement> derive from IEnumerable<TElement> but you can't cast an IOrderedEnumerable to an IGrouping.
If all you want is to write out the values, then Func could be:
public IEnumerable<Item> Func(IGrouping<int, Item> list)
{
return list.OrderBy(x => x.Val2);
}
and the foreach loop could be:
foreach(var group in grouped)
{
var orderedList = p.Func(group);
Console.WriteLine($"group: {group.Key}");
foreach (var value in orderedList)
{
Console.WriteLine($" {value.Val2}");
}
}
Hopefully this helps.

C# Splitting a List<string> Value

I have a List with values {"1 120 12", "1 130 22", "2 110 21", "2 100 18"}, etc.
List<string> myList = new List<string>();
myList.Add("1 120 12");
myList.Add("1 130 22");
myList.Add("2 110 21");
myList.Add("2 100 18");
I need to count based on the first number (ID) is and sum the consequent values for this
IDs i.e. for ID = 1 -> 120+130=150 and 12+22=34 and so on... I have to return an array with these values.
I know I can get these individual values, add them to an array and split it by the empty space between them with something like:
string[] arr2 = arr[i].Split(' ');
and loop thru them to do the sum of each value, but... is there an easy way to do it straight using Lists or Linq Lambda expression?
You can do it in LINQ like this:
var result = myList.Select(x => x.Split(' ').Select(int.Parse))
.GroupBy(x => x.First())
.Select(x => x.Select(y => y.Skip(1).ToArray())
.Aggregate(new [] {0,0}, (y,z) => new int[] {y[0] + z[0], y[1] + z[1]}));
First, the strings are split and converted to int, then they are grouped by ID, then the ID is dropped, and in the end, they are summed together.
But I strongly recommend not doing it in LINQ, because this expression is not easy to understand. If you do it the classic way with a loop, it is quite clear what is going on at first sight. But put this code containing the loop into a separate method, because that way it won't distract you and you still only call a one-liner as in the LINQ solution.
To do it straight, no LINQ, perhaps:
var d = new Dictionary<string, (int A, int B)>();
foreach(var s in myList){
var bits = s.Split();
if(!d.ContainsKey(bits[0]))
d[bits[0]] = (int.Parse(bits[1]), int.Parse(bits[2]));
else {
(int A, int B) x = d[bits[0]];
d[bits[0]] = (x.A + int.Parse(bits[1]), x.B + int.Parse(bits[2]));
}
}
Using LINQ to parse the int, and switching to using TryGetValue, will tidy it up a bit:
var d = new Dictionary<int, (int A, int B)>();
foreach(var s in myList){
var bits = s.Split().Select(int.Parse).ToArray();
if(d.TryGetValue(bits[0], out (int A, int B) x))
d[bits[0]] = ((x.A + bits[1], x.B + bits[2]));
else
d[bits[0]] = (bits[1], bits[2]);
}
Introducing a local function to safely get either the existing nums in the dictionary or a (0,0) pair might reduce it a bit too:
var d = new Dictionary<int, (int A, int B)>();
(int A, int B) safeGet(int i) => d.ContainsKey(i) ? d[i]: (0,0);
foreach(var s in myList){
var bits = s.Split().Select(int.Parse).ToArray();
var nums = safeGet(bits[0]);
d[bits[0]] = (bits[1] + nums.A, bits[2] + nums.B);
}
Is it any more readable than a linq version? Hmm... Depends on your experience with Linq, and tuples, I suppose..
I know this question already has a lot of answers, but I have not seen one yet that focuses on readability.
If you split your code into a parsing phase and a calculation phase, we can use LINQ without sacrificing readability or maintainability, because each phase only does one thing:
List<string> myList = new List<string>();
myList.Add("1 120 12");
myList.Add("1 130 22");
myList.Add("2 110 21");
myList.Add("2 100 18");
var parsed = (from item in myList
let split = item.Split(' ')
select new
{
ID = int.Parse(split[0]),
Foo = int.Parse(split[1]),
Bar = int.Parse(split[2])
});
var summed = (from item in parsed
group item by item.ID into groupedByID
select new
{
ID = groupedByID.Key,
SumOfFoo = groupedByID.Sum(g => g.Foo),
SumOfBar = groupedByID.Sum(g => g.Bar)
}).ToList();
foreach (var s in summed)
{
Console.WriteLine($"ID: {s.ID}, SumOfFoo: {s.SumOfFoo}, SumOfBar: {s.SumOfBar}");
}
fiddle
If you want, but I think it will be much easier to edit and optimize using the usual value. I don't find using this kind of logic inside LINQ will stay that way for a long period of time. Usually, we need to add more values, more parsing, etc. Make it not really suitable for everyday use.
var query = myList.Select(a => a.Split(' ').Select(int.Parse).ToArray())
.GroupBy(
index => index[0],
amount => new
{
First = amount[1],
Second = amount[2]
},
(index, amount) => new
{
Index = index,
SumFirst = amount.Sum(a => a.First),
SumSecond = amount.Sum(a => a.Second)
}
);
fiddle
is there an easy way to do it straight using Lists or Linq Lambda expression?
Maybe, is it wise to do this? Probably not. Your code will be hard to understand, impossible to unit test, the code will probably not be reusable, and small changes are difficult.
But let's first answer your question as a one LINQ statement:
const char separatorChar = ' ';
IEnumerable<string> inputText = ...
var result = inputtext.Split(separatorChar)
.Select(text => Int32.Parse(text))
.Select(numbers => new
{
Id = numbers.First()
Sum = numbers.Skip(1).Sum(),
});
Not reusable, hard to unit test, difficult to change, not efficient, do you need more arguments?
It would be better to have a procedure that converts one input string into a proper object that contains what your input string really represents.
Alas, you didn't tell us if every input string contains three integer numbers, of that some might contain invalid text, and some might contain more or less than three integer numbers.
You forgot to tell use what your input string represents.
So I'll just make up an identifier:
class ProductSize
{
public int ProductId {get; set;} // The first number in the string
public int Width {get; set;} // The 2nd number
public int Height {get; set;} // The 3rd number
}
You need a static procedure with input a string, and output one ProductSize:
public static ProductSize FromText(string productSizeText)
{
// Todo: check input
const char separatorChar = ' ';
var splitNumbers = productSizeText.Split(separatorChar)
.Select(splitText => Int32.Parse(splitText))
.ToList();
return new ProductSize
{
ProductId = splitNumbers[0],
Width = splitNumbers[1],
Height = splitNumbers[2],
};
}
I need to count based on the first number (ID) is and sum the consequent values for this IDs
After creating method ParseProductSize this is easy:
IEnumerable<string> textProductSizes = ...
var result = textProductSizes.Select(text => ProductSize.FromText(text))
.Select(productSize => new
{
Id = productSize.Id,
Sum = productSize.Width + productSize.Height,
});
If your strings do not always have three numbers
If you don't have always three numbers, then you won't have Width and Height, but a property:
IEnumerable<int> Numbers {get; set;} // TODO: invent proper name
And in ParseProductSize:
var splitText = productSizeText.Split(separatorChar);
return new ProductSize
{
ProductId = Int32.Parse(splitText[0]),
Numbers = splitText.Skip(1)
.Select(text => Int32.Parse(text));
I deliberately keep it an IEnumerable, so if you don't use all Numbers, you won't have parsed numbers for nothing.
The LINQ:
var result = textProductSizes.Select(text => ProductSize.FromText(text))
.Select(productSize => new
{
Id = productSize.Id,
Sum = productSize.Numbers.Sum(),
});

Linq items in a list exist in another list

I have 2 lists
List 1
var hashTags = new List<HashTag>();
hashTags.Add(new HashTag
{
Name = "#HashTag1",
Index = 1
});
hashTags.Add(new HashTag
{
Name = "#HashTag2",
Index = 2
});
hashTags.Add(new HashTag
{
Name = "#HashTag3",
Index = 3
});
hashTags.Add(new HashTag
{
Name = "#HashTag4",
Index = 4
});
List 2
var hashTags2 = new List<HashTag>();
hashTags2.Add(new HashTag
{
Name = "#HashTag1",
Index = 1
});
hashTags2.Add(new HashTag
{
Name = "#HashTag3",
Index = 3
});
hashTags2.Add(new HashTag
{
Name = "#HashTag4",
Index = 4
});
How do I check if all the elements in hashTags2 exist in hashTags? The index can be ignored and only the name matching is crucial. I can write a for loop to check element but I am looking for a LINQ solution.
Simple linq approach.
hashTags2.All(h=> hashTags.Any(h1 => h1.Name == h.Name))
Working Demo
As only equality of the names is to be taken into account, the problem can be solved by first mapping to the names and then checking containment as follows.
var hashTags2Names = hashTags2.Select( iItem => iItem.Name );
var hashTagsNames = hashTags.Select( iItem => iItem.Name );
var Result = hashTags2Names.Except( hashTagsNames ).Any();
So you want a boolean linq expression that returns true if the name of every element in hashTags2 exists in hashTags?
For this you want the function Enumerable.All, you want that every Hashtag in hashTags2 ...
bool result = hashTags2.All(hashTag => ...)
what do you want to check for every hashTag in hashTags2? That the name is a name in hashTags. So we need the names of hashTags:
IEnumerable<string> names = hashTags.Select(hashTag => hashTag.Name);
and to check if an item is in a sequence: Enumerable.Contains.
Put it all together:
IEnumerable<string> names = hashTags.Select(hashTag => hashTag.Name);
bool result = hashTags2.All(hashTag => names.Contains(hashTag.Name));
Of if you want one fairly unreadable expression:
bool result = hashTags2.All(hashTagX =>
hashTags.Select(hashTagY => hashTagY.Name)
.Contains(hashtagX)))
Because of delayed execution there is no difference between the first and the second method. The first one will be more readable.
With Linq to objects you will need at least one IEqualityComparar, to
tell linq how to compare objects and to determine when they are equal.
A simple comparer would be the following that uses the Name property to determine equality of your HashTag.
public class NameEquality : IEqualityComparer<HashTag>
{
public bool Equals(HashTag tag, HashTag tag2)
{
return tag.Name == tag2.Name;
}
public int GetHashCode(HashTag tag)
{
return tag.Name.GetHashCode();
}
}
With this Equality Comparer you can use the linq method Except(), to get all Elements from your list hashTag that are not part of hashTag2.
hashTags.Except(hashTags2, new NameEquality())
I prefer the join operator, however it is just a matter of taste, I guess:
var hashMatched = hashTags.Join(hashTags2,_o => _o.Name, _i => _i.Name, (_o,_i) => _o);

Check if list<t> contains any of another list

I have a list of parameters like this:
public class parameter
{
public string name {get; set;}
public string paramtype {get; set;}
public string source {get; set;}
}
IEnumerable<Parameter> parameters;
And a array of strings i want to check it against.
string[] myStrings = new string[] { "one", "two"};
I want to iterate over the parameter list and check if the source property is equal to any of the myStrings array. I can do this with nested foreach's but i would like to learn how to do it in a nicer way as i have been playing around with linq and like the extension methods on enumerable like where etc so nested foreachs just feel wrong. Is there a more elegant preferred linq/lambda/delegete way to do this.
Thanks
You could use a nested Any() for this check which is available on any Enumerable:
bool hasMatch = myStrings.Any(x => parameters.Any(y => y.source == x));
Faster performing on larger collections would be to project parameters to source and then use Intersect which internally uses a HashSet<T> so instead of O(n^2) for the first approach (the equivalent of two nested loops) you can do the check in O(n) :
bool hasMatch = parameters.Select(x => x.source)
.Intersect(myStrings)
.Any();
Also as a side comment you should capitalize your class names and property names to conform with the C# style guidelines.
Here is a sample to find if there are match elements in another list
List<int> nums1 = new List<int> { 2, 4, 6, 8, 10 };
List<int> nums2 = new List<int> { 1, 3, 6, 9, 12};
if (nums1.Any(x => nums2.Any(y => y == x)))
{
Console.WriteLine("There are equal elements");
}
else
{
Console.WriteLine("No Match Found!");
}
If both the list are too big and when we use lamda expression then it will take a long time to fetch . Better to use linq in this case to fetch parameters list:
var items = (from x in parameters
join y in myStrings on x.Source equals y
select x)
.ToList();
list1.Select(l1 => l1.Id).Intersect(list2.Select(l2 => l2.Id)).ToList();
var list1 = await _service1.GetAll();
var list2 = await _service2.GetAll();
// Create a list of Ids from list1
var list1_Ids = list1.Select(l => l.Id).ToList();
// filter list2 according to list1 Ids
var list2 = list2.Where(l => list1_Ids.Contains(l.Id)).ToList();

Using LINQ to extract ints from a list of strings

I have a List<string>, and some of these strings are numbers. I want to extract this subset into a List<int>.
I have done this in quite a verbose looking way - as below - but I get the feeling there must be a neater LINQ way to structure this. Any ideas?
List<string> myStrs = someListFromSomewhere;
List<int> myInts = new List<int>();
foreach (string myStr in myStrs)
{
int outInt;
if (int.TryParse(myStr, out outInt))
{
myInts.Add(outInt);
}
}
Obviously I don't need a solution to this - it's mainly for my LINQ education.
You can do it like this:
int parsed = 0;
myInts = myStrs.Where(x => int.TryParse(x, out parsed)).Select(x => parsed);
This works because the execution of LINQ operators is deferred, meaning:
For each item in myStrs first the code in Where is executed and the result written into parsed. And if TryParse returned true the code in Select is executed. This whole code for one item runs before this whole code is run for the next item.
LINQ and out parameters don't mix well. You could do this:
var myInts = myStrs
.Select(s =>
{
int outInt;
return int.TryParse(s, out outInt) ? (int?)outInt : null;
})
.Where(i => i.HasValue)
.Select(i => i.Value)
.ToList();
Since this is for LINQ education... If you really are looking for how this can be done with only LINQ syntax, Another option is to move the parsing logic to a class that encapsulates the result and the value.
var myInts = from val in myStrs
let parserVal = new Parser(val)
where parserVal.IsInt
select parserVal.Val;
where Parser is something like this...
class Parser
{
public bool IsInt { get; set; }
public int Val { get; set; }
public Parser(string val)
{
int outVal;
IsInt = int.TryParse(val, out outVal);
if (IsInt)
{
Val = outVal;
}
}
}
Select distinct then parse each selected item
List<int> myInts = new List<int>();
myInts = myStrs.Distinct().Select(
x =>
{
int parsed;
int.TryParse(x, out parsed);
return parsed;
})
.ToList();

Categories

Resources