I have an issue here a bit complex than I'm trying to resolve since some days ago. I'm using the PetaPoco ORM and didn't found any other way to do a complex query like this:
var data = new List<string[]>();
var db = new Database(connectionString);
var memberCapabilities = db.Fetch<dynamic>(Sql.Builder
.Select(#"c.column_name
,CASE WHEN c.is_only_view = 1
THEN c.is_only_view
ELSE mc.is_only_view end as is_only_view")
.From("capabilities c")
.Append("JOIN members_capabilities mc ON c.capability_id = mc.capability_id")
.Where("mc.member_id = #0", memberID)
.Where("c.table_id = #0", tableID));
var roleCapabilities = db.Fetch<dynamic>(Sql.Builder
.Select(#"c.column_name
,CASE WHEN c.is_only_view = 1
THEN c.is_only_view
ELSE rc.is_only_view end as is_only_view")
.From("capabilities c")
.Append("JOIN roles_capabilities rc ON c.capability_id = rc.capability_id")
.Append("JOIN members_roles mr ON rc.role_id = mr.role_id")
.Where("mr.member_id = #0", memberID)
.Where("c.table_id = #0", tableID));
I'm trying to get the user capabilities, but my system have actually to ways to assign an user a capability, or direct to that user or attaching the user to a role. I wanted to get this merged list using a stored procedure but I needed cursors and I thought maybe should be easier and faster doing this on the web application. So I get that two dynamics and the members capabilities have priority to the roles capabilities, so I need to check if that using loops. And I did like this:
for (int i = 0; i < roleCapabilities.Count; i++)
{
bool added = false;
for (int j = 0; j < memberCapabilities.Count; j++)
if (roleCapabilities[i].column_name == memberCapabilities[j].column_name)
{
data.Add(new string[2] { memberCapabilities[j].column_name, Convert.ToString(memberCapabilities[j].is_only_view) });
added = true;
break;
}
if (!added)
data.Add(new string[2] { roleCapabilities[i].column_name, Convert.ToString(roleCapabilities[i].is_only_view) });
}
So now the plan is delete the duplicate entries. I have try using the following methods with no results:
data = data.Distinct();
Any help? Thanks
Make sure that your object either implements System.IEquatable or overrides Object.Equals and Object.GetHashCode. In this case, it looks like you're storing the data as string[2], which won't give you the desired behavior. Create a custom object to hold the data, and do one of the 2 options listed above.
If I understand your question correctly you want to get a distinct set of arrays of strings, so if the same array exists twice, you only want one of them? The following code will return arrays one and three while two is removed as it is the same as one.
var one = new[] {"One", "Two"};
var two = new[] {"One", "Two"};
var three = new[] {"One", "Three"};
List<string[]> list = new List<string[]>(){one, two, three};
var i = list.Select(l => new {Key = String.Join("|", l), Values = l})
.GroupBy(l => l.Key)
.Select(l => l.First().Values)
.ToArray();
You might have to use ToList() after Distinct():
List<string[]> distinct = data.Distinct().ToList();
Related
I have to take the same values from multiple source and so I used Concat but I have large number of fields and couple of more sources too.
IEnumerable<Parts> partsList = (from parts in xml.XPathSelectElements("//APS/P")
select new WindchillPart
{
Code = (string)parts.Element("Number"),
Part = (string)parts.Element("KYZ"),
Name = (string)parts.Element("Name"),
})
.Concat(from uparts in xml.XPathSelectElements("//APS/U")
select new WindchillPart
{
Code = (string)uparts.Element("Number"),
Part = (string)uparts.Element("KYZ"),
Name = (string)uparts.Element("Name"),
});
I almost have 15 fields and 5 sources. So is there anyway to make the fields as common and just add the sources somewhere to work and simplify this?
You could create an array of all your pathes, and use SelectMany to get the elements. In the end, you call Select just once:
string[] pathes = new string[] { "//APS/P", "//APS/U" };
IEnumerable<Parts> partsList = pathes.SelectMany(path => xml.XPathSelectElements(path)).
Select(uparts => new WindchillPart
{
Code = (string)uparts.Element("Number"),
Part = (string)uparts.Element("KYZ"),
Name = (string)uparts.Element("Name"),
});
I am new to c# programming and would like to know how we can read data from an excel cell by cell. In the below code, I am getting an array of data from Column A of excel as pValue1= a;b;c;d%;e%;f%; Now, I want to push only the values with % at the end into a different array if the header of column A=ID. Also, I want to enclose each item in pValue1 with single quotes.
Input:
ID
Name
a
roy
b
may
c
Jly
d%
nav
e%
nov
f%
lio
Expected output:
pValue1= 'a';'b';'c'
pValue3= d%e%f%
try {
Utils.ExcelFile excelFile = new Utils.ExcelFile(excelFilename);
DataTable excelData = excelFile.GetDataFromExcel();
// Column headers
param1 = 0 < excelData.Columns.Count ? excelData.Columns[0].ColumnName :string.Empty;
param2 = 1 < excelData.Columns.Count ? excelData.Columns[1].ColumnName :string.Empty;
ArrayList pValueArray1 = new ArrayList();
ArrayList pValueArray2 = new ArrayList();
if (pValueArray1.Count > 0) pValue1 = string.Join(";", pValueArray1.ToArray()) + ";";
if (pValueArray2.Count > 0) pValue2 = string.Join(";", pValueArray2.ToArray()) + ";";
}
Not sure if i understood your issue. I guess you have already loaded the excel into the DataTable and you now just want to split the Id-column into two separate lists. You can use LINQ:
var percentLookup = excelData.AsEnumerable()
.ToLookup(row => row.Field<string>("Id").EndsWith("%"));
List<string> pValue1List = percentLookup[false]
.Select(row => row.Field<string>("Id"))
.ToList();
List<string> pValue2List = percentLookup[true]
.Select(row => row.Field<string>("Id"))
.ToList();
The lookup contains two groups, the rows where the id-column has a percent at the end and the rest. So you can create the two lists easily with it.
Since you are new to C# programming it might be better to use a plain loop:
List<string> pValue1List = new List<string>();
List<string> pValue2List = new List<string>();
foreach (DataRow row in excelData.Rows)
{
string id = row.Field<string>("Id");
if(id.EndsWith("%"))
{
pValue2List.Add(id);
}
else
{
pValue1List.Add(id);
}
}
If you need a String[] instead of a List<string> use ToArray instead of ToList and in the second approach fill the lists but use i.e. pValue1List.ToArray at the end.
In general: you should stop using ArrayList, that's 20 years old and since more than 10 years obsolete. Use a strongly typed List<T>, here a List<string>.
I have a master list that has the values for tables of a database I know to be correct:
masterList: List<string>(){ "business", "customer", "location", "employee", etc}
And I've queried a new database that is supposed to be identical. My test will tell me if I have any errors in the scripts my team has made to make this new DB. tablesList is supposed to the be the return of my query:
tablesList: List<string>(){ "business", "customer", "location", "employee", etc}
So in practice they are supposed to be the same, but to test errors, I want to compare the tablesList against the masterList to make sure all needed tables are there. As a copy of this process, I'm also reversing the search, in case there are any extra tables that are not there on the masterList.
Question: How do I compare a list against a master list, and return items that don't match up?
I am using Visual Studio 2017 with c# .net Core 2.0.
Here is what I've been trying so far:
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item)));
But with this, I'm getting the error:
cannot convert from IEnumerable to string
You can get the two directions of errors using LINQ. No need for the loop:
var missingInMasterList = tableList.Where(x => !masterList.Contains(x)).ToList();
var missingInTableList = masterList.Where(x => !tableList.Contains(x)).ToList();
Are you looking for something like that;
var errorList = tableList.Where(x => !masterList.Contains(x));
You can capture the differences using .Except(), which is one of the IEnumerable set operations:
var missingTables = masterList.Except(tablesList);
var extraTables = tablesList.Except(masterList);
Then, to create your error message, you can join the items in these IEnumerables with a comma into a single string using string.Join():
var errorMessage = new StringBuilder();
if (missingTables.Any())
{
errorMessage.AppendLine("Missing Tables: " + string.Join(", ", missingTables));
}
if (extraTables.Any())
{
errorMessage.AppendLine("Extra Tables: " + string.Join(", ", extraTables));
}
Then you can output your results by checking the length of errorMessage to determine if any errors were encountered:
if (errorMessage.Length > 0)
{
Console.WriteLine(errorMessage.ToString());
}
else
{
Console.WriteLine("No extra or missing tables detected");
}
I think better to use is Except() as follows
var MasterList = new List<string> { "business", "customer", "location", "employee"};
var ChildList = new List<String> { "customer", "location", "employee" };
var filter = MasterList.Except(ChildList);
This will values those are not in ChildList.You can also do vice versa.
To find all items that are in the tablesList but not in the masterList use .Contains:
var errorsList = tableList.Where(x => !masterList.Contains(x));
But I recommend you use a HashSet<String> for masterList so search for an item in it will be in O(1) instead of O(n):
var masterCollection = new HashSet<String>(GrabMasterTableList());
var errorsList = tableList.Where(x => !masterCollection.Contains(x));
As for the problem with your code as you posted:
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item))); // <-- error
As the error points out, Linq's .Where returns an IEnumerable<T> whereas .Add expects a single item of the type of the collection, which in this case is a single string. You could use .AddRange instead but I think a better use all together is what I wrote above.
Your code is presently trying to add an IEnumerable to a List.
If you want to add all the matches you should AddRange instead.
https://msdn.microsoft.com/en-us/library/z883w3dc(v=vs.110).aspx
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.AddRange(tablesList.Where(x => x.Contains(item)));
I've a list that pulls timespans and selects and displays the largest timespan in the list. I need to figure out a way to display the name that also matches that timespan. Not exactly sure where to start with this. would anyone be able to get me started. some of the code I've is below.
Code: You can see I create a list of timespans from item where workmodedirectiondescription = AVAIL-IN. I need AgName = AVAIL-IN also = and then to match the agent that has the max time Listoftimespans.max and display the name of the agent that has the max time. I hope this makes sense. The biggest issue I'm having is I can create a separate list of agname but then don't know how to match the times of the agent to display the name that has the largest time. I can't add agname to the list listoftimespans because agname is of type string not timespan.
var listofTimeSpans = new List<TimeSpan>();
List<NewAgent> newAgentList = new List<NewAgent>();
foreach (var item in e.CmsData.Agents)
{
NewAgent newAgents = new NewAgent();
newAgents.AgentName = item.AgName;
newAgents.AgentExtension = item.Extension;
newAgents.AgentDateTimeChange = ConvertedDateTimeUpdated;
newAgents.AuxReasons = item.AuxReasonDescription;
newAgents.LoginIdentifier = item.LoginId;
newAgents.AgentState = item.WorkModeDirectionDescription;
var timeSpanSince = DateTime.Now - item.DateTimeUpdated;
newAgents.AgentDateTimeStateChange = timeSpanSince;
newAgentList.Add(newAgents);
if (item.WorkModeDirectionDescription == "AVAIL-IN")
{
listofTimeSpans.Add(timeSpanSince);
}
var comparetimeandname = new tuple<string, TimeSpan>(item.Agname, listoftimespans.max());
max = comparetimeandname.item2;
maxname = comparetimeandname.item1.
}
Update: occurs right after the above code
var availInAgents = newAgentList.Where(ag => ag.AgentState == "AVAIL-IN").ToList();
availInAgents.Sort((t1, t2)=>t1.AgentDateTimeStateChange.CompareTo(t2.AgentDateTimeStateChange));
var minTimeSpanAgent = availInAgents.FirstOrDefault();
var maxTimeSpanAgent = availInAgents.LastOrDefault();
var min3 = availInAgents.Take(3).ToList();
var max3 = availInAgents.Reverse<NewAgent>().Take(3).ToList();
NextInLine.Text = string.Join(Environment.NewLine, min3);
Edit: screenshot
enter image description here
So let's say, as discussed in comments, you collect a List of tuples representing names and their timespans. By the way, I wouldn't do that in the loop; I'd do this after the loop:
var availInItems =
newAgentList
.Where(ag => ag.WorkModeDirectionDescription == "AVAIL-IN")
.Select(ag =>
new Tuple<String, TimeSpan>(ag.AgentName, ag.AgentDateTimeStateChange))
.ToList();
To get the min and max timespan items, just sort the list by the TimeSpan values of each item, and grab the first and last items. I'm using FirstOrDefault() and LastOrDefault() instead of First() and Last() because they return null on an empty list rather than throwing an exception. That way, we more gracefully handle the case where the list happens to be empty.
List<Tuple<String, TimeSpan>> availInItems = new List<Tuple<string, TimeSpan>>();
// ... populate availInItems in loop ...
availInItems.Sort((t1, t2) => t1.Item2.CompareTo(t2.Item2));
var minTimeSpanTuple = availInItems.FirstOrDefault();
var maxTimeSpanTuple = availInItems.LastOrDefault();
Lowest three, highest three:
var min3 = availInItems.Take(3).ToList();
// There's another overload of Reverse, without the type argument, which
// is a meber of List<T>; it reorders the list in place and returns void.
// You need this one.
var max3 = availInItems.Reverse<Tuple<String, TimeSpan>>().Take(3).ToList();
Those are OK if you have fewer than three items in the list. You'll just get whatever it has.
However
Both pieces of information that we're putting in the tuple are in NewAgent. Why get a tuple involved? No need. I'd just do this:
var availInAgents =
newAgentList
.Where(ag => ag.WorkModeDirectionDescription == "AVAIL-IN")
.ToList();
availInAgents.Sort((t1, t2) =>
t1.AgentDateTimeStateChange.CompareTo(t2.AgentDateTimeStateChange));
var minTimeSpanAgent = availInAgents.FirstOrDefault();
var maxTimeSpanAgent = availInAgents.LastOrDefault();
var min3 = availInAgents.Take(3).ToList();
var max3 = availInAgents.Skip(availInAgents.Count - 3).ToList();
max3.Reverse();
I think LINQ can help you.
For example, if you need the agent with max TimeSpan:
var maxAgent = newAgentList.Max(agnet => agnet.AgentDateTimeStateChange);
Console.writeLine(maxAgent.AgentName);
I am trying to sort a collection of objects in C# by a custom property.
(For context, I am working with the Twitter API using the Twitterizer library, sorting Direct Messages into conversation view)
Say a custom class has a property named label, where label is a string that is assigned when the class constructor.
I have a Collection (or a List, it doesn't matter) of said classes, and I want to sort them all into separate Lists (or Collections) based on the value of label, and group them together.
At the moment I've been doing this by using a foreach loop and checking the values that way - a horrible waste of CPU time and awful programming, I know. I'm ashamed of it.
Basically I know that all of the data I have is there given to me, and I also know that it should be really easy to sort. It's easy enough for a human to do it with bits of paper, but I just don't know how to do it in C#.
Does anyone have the solution to this? If you need more information and/or context just ask.
Have you tried Linq's OrderBy?
var mySortedList = myCollection.OrderBy(x => x.PropertyName).ToList();
This is still going to loop through the values to sort - there's no way around that. This will at least clean up your code.
You say sorting but it sounds like you're trying to divide up a list of things based on a common value. For that you want GroupBy.
You'll also want ToDictionary to switch from an IGrouping as you'll presumably be wanting key based lookup.
I assume that the elements within each of the output sets will need to be sorted, so check out OrderBy. Since you'll undoubtedly be accessing each list multiple times you'll want to collapse it to a list or an array (you mentioned list) so I used ToList
//Make some test data
var labels = new[] {"A", "B", "C", "D"};
var rawMessages = new List<Message>();
for (var i = 0; i < 15; ++i)
{
rawMessages.Add(new Message
{
Label = labels[i % labels.Length],
Text = "Hi" + i,
Timestamp = DateTime.Now.AddMinutes(i * Math.Pow(-1, i))
});
}
//Group the data up by label
var groupedMessages = rawMessages.GroupBy(message => message.Label);
//Convert to a dictionary for by-label lookup (this gives us a Dictionary<string, List<Message>>)
var messageLookup = groupedMessages.ToDictionary(
//Make the dictionary key the label of the conversation (set of messages)
grouping => grouping.Key,
//Sort the messages in each conversation by their timestamps and convert to a list
messages => messages.OrderBy(message => message.Timestamp).ToList());
//Use the data...
var messagesInConversationA = messageLookup["A"];
var messagesInConversationB = messageLookup["B"];
var messagesInConversationC = messageLookup["C"];
var messagesInConversationD = messageLookup["D"];
It sounds to me like mlorbetske was correct in his interpretation of your question. It sounds like you want to do grouping rather than sorting. I just went at the answer a bit differently
var originalList = new[] { new { Name = "Andy", Label = "Junk" }, new { Name = "Frank", Label = "Junk" }, new { Name = "Lisa", Label = "Trash" } }.ToList();
var myLists = new Dictionary<string, List<Object>>();
originalList.ForEach(x =>
{
if (!myLists.ContainsKey(x.Label))
myLists.Add(x.Label,new List<object>());
myLists[x.Label].Add(x);
});