I have the following code:
var accidents = text.Skip(NumberOfAccidentsLine + 1).Take(numberOfAccidentsInFile).ToArray();
where accidents is an array of strings.
I want to make a Linq transformation from the string array to an array of Accident objects as follows:
return accidents.Select(t => new Accident() {Id = i, Name = t.Replace("\"", string.Empty)}).ToArray();
How do I retrieve the index i from the accidents array using Linq or do I have to go old school?
I'm not sure what kind of index you're looking for, but if it's just set of consecutive numbers then you're lucky. There is Select overload that does exactly that:
return accidents.Select((t, i) => new Accident() {Id = i, Name = t.Replace("\"", string.Empty)}).ToArray();
It expects a delegate that takes two parameters - the item and its index.
Use Enumerable.Range to generate the ID values and then use the current value to index into your String Array:
Enumerable.Range(0, accidents.Length).Select(f => new Accident() { Id = f, Name = accidents[f] })
May be this LINQ query will help you to find The formated name with Index:
var accidents=(from acc in accidents
select new {
id=accidents.IndexOf(acc),
Name = acc.Replace("\"", string.Empty)
}).ToArray()
or you can also use .ToList() for the case if you want result to be in IEnumerable format.
Related
I am using SQLite.NET. I am trying to pass an integer array to a search query. I would like to return a list of records where the IDs (table attributes) match the integers in an array. The array is of size 10.
In MainPage.cs:
int[] arr = new int[10];
// for testing purposes
for (int i = 0; i <= 9; i++{
arr[i] = i;
}
var selected = App.Database.SearchList(arr);
In SQLiteDatabase.cs:
public List<Rooms> SearchList(int[] ID)
{
return database.Query<Rooms>("SELECT * FROM Rooms WHERE ID = ?;", ID);
}
This results in the following error when the query is executed:
System.NotSupportedException: Timeout exceeded getting exception details
How can I return a list of records where the IDs match? Thank you.
use the IN keyword
WHERE ID IN (1, 2, 3, 4, 5)
Apparently SQLite.NET does not support passing in an array as a parameter.
This throws and exception:
var IDs = new[] { 1, 2 };
return database.Query<Rooms>("SELECT * FROM Rooms WHERE ID IN (?);", IDs);
In this case the only solution is to join the paramters in the SQL string. This works for int[], but when using strings you should beware of SQL injection.
var IDs = new[] { 1, 2 };
return database.Query<Rooms>($"SELECT * FROM Rooms WHERE ID IN ({string.Join(",", IDs)});");
You need to have 1 '?' per argument so you can create your query string like this:
string query = $"SELECT * FROM Rooms WHERE ID IN ({string.Join(",", IDs.Select(x => "?"))});"
This makes the IN clause like (?,?,...,?)
Then when calling Query its necessary to cast the arguments to an object array or you'll get the exception as mentioned by #Neil B like this:
return database.Query<Rooms>(query,IDs.Cast<object>().ToArray());
How can I get the string from a list that best match with a base string using the Levenshtein Distance.
This is my code:
{
string basestring = "Coke 600ml";
List<string> liststr = new List<string>
{
"ccoca cola",
"cola",
"coca cola 1L",
"coca cola 600",
"Coke 600ml",
"coca cola 600ml",
};
Dictionary<string, int> resultset = new Dictionary<string, int>();
foreach(string test in liststr)
{
resultset.Add(test, Ldis.Compute(basestring, test));
}
int minimun = resultset.Min(c => c.Value);
var closest = resultset.Where(c => c.Value == minimun);
Textbox1.Text = closest.ToString();
}
In this example if I run the code I get 0 changes in string number 5 from the list, so how can I display in the TextBox the string itself?
for exemple : "Coke 600ml" Right now my TextBox just returns:
System.Linq.Enumerable+WhereEnumerableIterator`1
[System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]]
Thanks.
Try this
var closest = resultset.First(c => c.Value == minimun);
Your existing code is trying to display a list of items in the textbox. I looks like it should just grab a single item where Value == min
resultset.Where() returns a list, you should use
var closest = resultset.First(c => c.Value == minimun);
to select a single result.
Then the closest is a KeyValuePair<string, int>, so you should use
Textbox1.Text = closest.Key;
to get the string. (You added the string as Key and changes count as Value to resultset earilier)
There is a good solution in code project
http://www.codeproject.com/Articles/36869/Fuzzy-Search
It can be very much simplified like so:
var res = liststr.Select(x => new {Str = x, Dist = Ldis.Compute(basestring, x)})
.OrderBy(x => x.Dist)
.Select(x => x.Str)
.ToArray();
This will order the list of strings from most similar to least similar.
To only get the most similar one, simply replace ToArray() with First().
Short explanation:
For every string in the list, it creates an anonymous type which contains the original string and it's distance, computed using the Ldis class. Then, it orders the collection by the distance and maps back to the original string, so as to lose the "extra" information calculated for the ordering.
I have the following ItemArray:
dt.Rows[0].ItemArray.. //{0,1,2,3,4,5}
the headers are : item0,item1,item2 etc..
So far, to get a value from the ItemArray I used to call it by an index.
Is there any way to get the value within the ItemArray with a Linq expression based on the column name?
Thanks
You can also use the column-name to get the field value:
int item1 = row.Field<int>("Item1");
DataRow.Item Property(String)
DataRow.Field Method: Provides strongly-typed access
You could also use LINQ-to-DataSet:
int[] allItems = (from row in dt.AsEnumerable()
select row.Field<int>("Item1")).ToArray();
or in method syntax:
int[] allItems = dt.AsEnumerable().Select(r => r.Field<int>("Item1")).ToArray();
If you use the Item indexer rather than ItemArray, you can access items by column name, regardless of whether you use LINQ or not.
dt.Rows[0]["Column Name"]
Tim Schmelter's answer is probably what you are lookin for, just to add also this way using Convert class instead of DataRow.Field:
var q = (from row in dataTable.AsEnumerable() select Convert.ToInt16(row["COLUMN1"])).ToArray();
Here's what I've come up with today solving a similar problem. In my case:
(1)I needed to xtract the values from columns named Item1, Item2, ... of bool type.
(2) I needed to xtract the ordinal number of that ItemN that had a true value.
var itemValues = dataTable.Select().Select(
r => r.ItemArray.Where((c, i) =>
dataTable.Columns[i].ColumnName.StartsWith("Item") && c is bool)
.Select((v, i) => new { Index = i + 1, Value = v.ToString().ToBoolean() }))
.ToList();
if (itemValues.Any())
{
//int[] of indices for true values
var trueIndexArray = itemValues.First().Where(v => v.Value == true)
.Select(v => v.Index).ToArray();
}
forgot an essential part: I have a .ToBoolean() helper extension method to parse object values:
public static bool ToBoolean(this string s)
{
if (bool.TryParse(s, out bool result))
{
return result;
}
return false;
}
I have this:
// Load changelog types
ChangeLogType[] Types = ChangeLogFunctions.GetAllChangelogTypes();
foreach(ChangeLogType Rec in Types){
ListItem N = new ListItem();
N.Text = Rec.Type;
N.Value = Rec.ID.ToString();
LstChangeLogType.Items.Add(N);
}
It calls a function that returns an array of ChangeLogTypes, and then adds each one into a list control. Is there a more elegant way of doing this? I feel I'm repeating code each time I do this or something similar.
Yup, LINQ to Objects is your friend:
var changeLogTypes = ChangeLogFunctions.GetAllChangelogTypes()
.Select(x => new ListItem {
Text = x.Type,
Value = x.ID.ToString() })
.ToList();
The Select part is projecting each ChangeLogType to a ListItem, and ToList() converts the resulting sequence into a List<ListItem>.
This is assuming you really wanted a new list with all these entries. If you need to add the results to an existing list, you'd do that without the ToList call, but calling AddRange on an existing list with the result of the Select call.
It's well worth learning more about LINQ in general and LINQ to Objects in particular - it can make all kinds of things like this much simpler.
var range = Types.Select(rec =>
new ListItem { Text = rec.Type, Value = rec.ID.ToString() });
LstChangeLogType.AddRange(range);
Linq?
LstChangeLogType.Items = Types.Select(x => new ListItem()
{ Text = x.Type, Value = x.ID.ToString() }).ToList();
using System.Linq;
var items = Types
.Select (rec => ListItem
{
Text = Rec.Type;
Value = Rec.ID.ToString();
}
LstChangeLogType.Items.AddRange(items);
Using some LINQ extension methods:
LstChangeLogType.AddItems.AddRange(
Types.Select(t =>
new ListItem() { Text = t.Type, Value = t.ID.ToString() }).ToArray());
I have the following code:
var data= from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new { Id= g.Key };
I would like to add 1 more element to the data variable. How would I do it? (is there something like a data.Concat(1) etc
If you want to add an additional Id you can indeed concat it:
data = data.Concat( new [] { new { Id= "1" } });
This works because anonymous types that have the same fields in the same order are compiled down to the same type.
You can return the LINQ result as a List<string>.
Generic lists can Add more itens easily.
var data = (from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new { Id = g.Key }).ToList();
data.Add(new { Id = "X" });
This way you will not need to declare another variable to hold the Enumerable with the new item (since an Enumerable is imutable and can't add new items to itself).
EDIT:
Like pointed, changing the Enumerable<T> to List<T> will put and hold all the elements on the memory, wich isn't a good performance approach.
To stay with the Enumerable<T>, you can do:
data = data.Concat(new [] { new { Id = "X" } });
Because a Enumerable<Anonymous> can be placed inside itself.
I don't know how with query syntax, but you can use Concat with a single value by creating a new array of values with a single item:
IEnumerable<int> data = GetData()
.Concat(new[] { "5" });
The problem with doing this simply is that your data is an IEnumerable<AnonymousType>, which you can't simply new up, and I don't think new anonymous types are compatible with each other. (Edit: According to BrokenGlass, they are compatible. You can try his solution instead).
If they aren't compatible, you could concat the item before your Select clause, but again, how do you create an item of that type.
The solution would probably be to select to an IEnumerable<string> first, concat, then re-select into your anonymous type:
var data = (from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select g.Key)
.Concat(new[] { "5" })
.Select(k => new { Id = key });
Or to create a new named structure for your result, and concatenate one of those:
var data = from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new MyCustomResult() { Id = g.Key };
data = data.Concat(new MyCustomResult() { Id = "5" });
You would need to change from an anonymous type to a known type and then add that new element.
// ...
select new MyResultClass { Id = g.Key };
data.Add(new MyResultClass { Id = 4 });