Select fails to print to console - c#

I was hoping to use Select as a functional foreach. When I do the following, I expected it to print
foo
bar
baz
it doesn't print anything however. Howcome? The code is
List<String> strings = new List<String>(){"foo", "bar", "baz"};
strings.Select(st => { Console.WriteLine(st); return 1; });

Use ForEach:
List<String> strings = new List<String>() { "foo", "bar", "baz" };
strings.ForEach(st => { Console.WriteLine(st); });
By using Select you're basically defining anonymous functions with the following body.
Console.WriteLine(st);
return 1;
So, Console.WriteLine will only be triggered when you're iterating through the list, like this:
var x= strings.Select(st => { Console.WriteLine(st); return 1; });
foreach (var i in x){ }
or x.ToList()
And that is wrong, use ForEach :)

Related

How to extract items at specified locations from list of comma separated strings using LINQ

I have a list of comma separated strings and I need to extract 1-st and 3-rd items from all strings.
List<string> list = new List<string>()
{
"1,2,3",
"4,5,6",
"7,8,9",
"10,11,12"
};
List<Tuple<string, string>> parsed = new List<Tuple<string, string>>(list.Count);
foreach (string s in list)
{
string[] items = s.Split(',');
parsed.Add(new Tuple<string, string>(items[0], items[2]));
}
Console.WriteLine(string.Join(Environment.NewLine, parsed.Select(p => p.Item1 +","+ p.Item2)));
Console.ReadLine();
That results:
1,3
4,6
7,9
10,12
But when I try to write it using LINQ, I can't get something simpler than:
IEnumerable<Tuple<string, string>> parsed = list.Select(
s =>
{
string[] items = s.Split(',');
return new Tuple<string, string>(items[0], items[2]);
});
I was wondering if it's possible to get rid of that {} block and replace it with LINQ function calls. To be clear, I am asking this question only to increase my knowledge of the features and capabilities of LINQ, so, any suggestion is welcome.
Edit:
So far, all suggested codes call the split function twice. Is there a way to get the desired result just by calling it once? Something like:
var parsed = list.Select(s => s.Split(',').Magic(...));
Also, by that code sample above, I didn't mean first and last items. I really mean items at specified locations.
If you are working with C#7 or above version, then you can write even in simpler manner,
IEnumerable<Tuple<string, string>> parsed = list.Select(
s => (s.Split(',')[0], s.Split(',')[2]));
You can do something like below
IEnumerable<Tuple<string, string>> parsed = list.Select(
s =>
{
var spl = s.Split(',');
return new Tuple<string, string>(spl[0], spl[2]);
// return new MyClass(spl[0], spl[2], ... ,spl[n]);
});
If you want the , separated list back by removing the middle number you can use the Regex to replace it.
IEnumerable<string> afterUpdate = list.Select(s => Regex.Replace(s, #",[0-9]*,", ","));
Output for this will be
{
"1,3",
"4,6",
"7,9",
"10,12"
};
May be this could help...
//----------------Linq.----------------------
//Data Source
var source = new List<string> { "1,2,3", "4,5,6", "7,8,9", "10,11,12" };
//var sourceTest = new List<string> { "11,45,6,5,", "2,3,4,5,6", "1,7,40,30", "10,20,30,40,50" };
//var sourceTest2 = new List<string> { "15,12,11,45,6,5,", "1,2,3,4,5,6", "1,7,9,40,30", "60,20,70,80,90,100" };
//Query Creation
var queryLambda = source.Select(item => new
{
FirstItem = item.Split(',').FirstOrDefault(),
ThirdItem = item.Split(',').Skip(2).FirstOrDefault()
}).ToList();
var query = (from items in source
select new
{
FirstItem = items.Split(',').FirstOrDefault(),
ThirdItem = items.Split(',').Skip(2).FirstOrDefault()
}).ToList();
//Query Execution
queryLambda.ForEach(item => { Console.WriteLine(string.Join(",", new string[] { item.FirstItem, item.ThirdItem })); });
Console.WriteLine();
query.ForEach(item => { Console.WriteLine(string.Join(",", new string[] { item.FirstItem, item.ThirdItem })); });
Console.ReadLine();

Enumerating two collections

class MyClass
{
string identifier;
}
I have two lists of identical count
List<MyClass> myClassList;
List<string> identifierList;
I would like to know what is the best way of assigning identifier in the myClassList enumerating over identifierList?
I wrote this, but looks too long(and inefficient?). There must be a better way of doing this?
identifierList.Select((value,index)=>new {value, index}).ToList().ForEach(x=> myClassList[x.index].identifier = x.value);
Yes, you're approach is inefficient(you're creating a throwaway list) and not very readable and also throws an exception if both lists have a different size.
You're looking for the Enumerable.Zip extension method which joins by index:
var zipped = myClassList.Zip(identifierList, (c, s) => new { class = c, string = s});
foreach(var x in zipped)
x.class.identifier = x.string;
You can use Zip:
myClassList.Zip(identifierList, (klass, id) =>
{
klass.identifier = id;
return klass
});
Note that this will give you a mutating Linq expression.
However, I suspect this is probably clearer:
for(int i = 0; i < myClassList.Count; i++)
{
myClassList[i].identifier = identifierList[i];
}
It may not be as "hip" as using Linq, but it's easier to read and understand what's going on!
Well, how about simple query like this.
myClassList.Select((e, i) => { e.identifier = identifierList[i]; return e; }).ToList();
We need this .ToList() to execute the query, otherwise it'll just do nothing.
Example :
List<string> identifierList = new List<string>()
{
"abc", "def", "ghi"
};
List<MyClass> myClassList = new List<MyClass>()
{
new MyClass(), new MyClass(), new MyClass(),
};
myClassList.Select((e, i) => { e.Id = identifierList[i]; return e; }).ToList();
foreach (var item in myClassList)
Console.Write(item.Id + " ");
Output : abc def ghi
In case your collections are of different length you can use :
myClassList.Select((e, i) => { e.Id = i < identifierList.Count? identifierList[i] : e.Id; return e; }).ToList();

How do I do a group my select by a fixed number using Linq to objects?

data.Select(object=> string.Format("<a>{0}</a>", object.LinkText))
.Select(html => string.Format("<div>{0}</div>", html))
.Aggregate((running, next) => running + next);
I have this query which basically turns some objects into html-markup. What I can´t seem to achieve is that the second select should only be run for every (fixed number) 3 elements in the first select. I wan´t my ouput to be something like this:
<div><a>xxx</a><a>yyy</a><a>zzz</a></div>
<div><a>ååå</a>....</div>
Please help me avoid a for-loop!
To group by 3, use this LINQ query:
var data = new[] {"quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"};
var res = data
.Select((s, i) => new { Link = string.Format("<a>{0}</a>", s), Index = i })
.GroupBy(p => p.Index/3)
.Select(g => string.Format("<div>{0}</div>", string.Join("", g.Select(v => v.Link))));
foreach (var re in res) {
Console.WriteLine(re);
}
The output of this program looks like this:
<div><a>quick</a><a>brown</a><a>fox</a></div>
<div><a>jumps</a><a>over</a><a>the</a></div>
<div><a>lazy</a><a>dog</a></div>
Note how this code uses string.Join instead of a slower Aggregate.
Of course since you use some other objects instead of strings, you will need to replace string.Format("<a>{0}</a>", s) with string.Format("<a>{0}</a>", s.LinkText).
Add in the after the first Select .Take(3)
The following should give you the desired result:
var data = new List<String>();
data.BreakIntoChunks(3).Select(html => string.Format("<div>{0}</div>", String.Join("", (html.Select(
item => string.Format("<a>{0}</a>", item))).ToArray())));
...
public static class EnumerableExt
{
public static IEnumerable<IEnumerable<TRecord>> BreakIntoChunks<TRecord>(this IEnumerable<TRecord> items,
int chunkSize)
{
int itemCount = 0;
var processedItems = new List<TRecord>();
foreach (TRecord record in items)
{
++itemCount;
processedItems.Add(record);
if (itemCount%chunkSize == 0)
{
yield return processedItems;
processedItems.Clear();
}
}
if (processedItems.Count != 0)
{
//Because- return the items which are not multiple of chunkSize
yield return processedItems;
}
}
}
Note: The result is generated in single iteration. That's the magic of yield return!

Parse string and order by parsed value

i am trying to build linq expression to solve my problem. I have list of strings
List<string> arr = new List<string>();
arr.Add("<desc><ru>1</ru><en>3</en></desc>");
arr.Add("<desc><ru>2</ru><en>4</en></desc>");
i want to parse every item and order results
fake sample:
arr.Select(ParseItem("en")).OrderBy(x)
then we have two items in ru in order 1,2
Thanks for all and sorry for my bad English
Thanks for all response but how to convert now results to IQueryable
class Test { public string data { get; set; } }
List<Test> arr = new List<Test>();
arr.Add(new Test { data = "<desc><ru>AAA</ru><en>One</en></desc>" });
arr.Add(new Test { data = "<desc><ru>1</ru><en>Two</en></desc>" });
arr.Add(new Test { data = "<desc><ru>22</ru><en>Ab</en></desc>" });
IQueryable<Test> t = arr.AsQueryable();
// here the trouble how to convert to IQueryable<Test>
t = t.Select(s => XElement.Parse(s.data)).Select(x => x.Element("en")).
OrderBy(el => el.Value);
Thanks again
After the question update - this will return your ordered data by <en> node value:
var result = arr
.OrderBy(t=>
XElement.Parse(t.data).Element("en").Value
);
The result valiable is of IOrderedEnumerable<Test> type.
This will produce a list of the values in ru tags (assuming they are integers), ordered by the values in en tags (again, assuming integers).
List<string> items = arr.Select(s => XElement.Parse(s))
.OrderBy(xml => (int)xml.Element("en"))
.Select(xml => (int)xml.Element("ru"))
.ToList();
If you simply want to enumerate, you can omit the ToList call:
foreach (var item in arr.Select(s => XElement.Parse(s))
.OrderBy(xml => (int)xml.Element("en"))
.Select(xml => (int)xml.Element("ru")))
{
// do something with item
}
I'm not sure I've got what the excepted results are, but if you need to select values in en ordered by the value in ru then here it is:
var orderedItems = (
from item in arr
let x = XElement.Parse(item)
let ruValue = (int)x.Element("ru")
let enValue = (int)x.Element("en")
orderby ruValue
select enValue
).ToList();
I don't know if it is too late, but if you are wanting to parse the text and if it is an integer then sort by value otherwise sort by text, then this might help.
You need to define a function like this to enable parsing in LINQ expressions:
Func<string, int?> tryParseInteger = text =>
{
int? result = null;
int parsed;
if (int.TryParse(text, out parsed))
{
result = parsed;
}
return result;
};
Then you can do queries like this:
var xs = new [] { "Hello", "3ff", "4.5", "5", };
var rs =
(from x in xs
select tryParseInteger(x)).ToArray();
// rs == new int?[] { null, null, null, 5, };
In your case you possibly want something like this:
var elements = new []
{
"<desc><ru>AAA</ru></desc>",
"<desc><ru>1</ru></desc>",
"<desc><ru>42</ru></desc>",
"<desc><ru>-7</ru></desc>",
"<desc><ru>BBB</ru></desc>",
"<desc><ru>22</ru></desc>",
};
var query =
from e in elements
let xe = XElement.Parse(e)
let v = xe.Element("ru").Value
orderby v
orderby tryParseInteger(v)
select v;
Which would give you:
{ "AAA", "BBB", "-7", "1", "22", "42" }
If you want to treat non-integers (ie parsed as null) to be zero then change the query by using this line:
orderby tryParseInteger(v) ?? 0
Then you'll get this:
{ "-7", "AAA", "BBB", "1", "22", "42" }
I hope this helps.

How can I compare two sets of data in linq and return the common data?

I have two IList<string> a and b. I want to find out what strings are in both a and b using LINQ.
Use Intersect:
Produces the set intersection of two sequences.
a.Intersect(b)
Example usage:
IList<string> a = new List<string> { "foo", "bar", "baz" };
IList<string> b = new List<string> { "baz", "bar", "qux" };
var stringsInBoth = a.Intersect(b);
foreach (string s in stringsInBoth)
{
Console.WriteLine(s);
}
Output:
bar
baz

Categories