Method to find / eliminate repetitive strings - c#

I am trying to find a solution, in eliminating repetitive string names, say for ex.,
in a literal field, i am populating names of the contributor of certain article's history version, and so, if "ron" has contributed to the versioning of an article 3 times, the name "ron" gets added to this literal control, and outputs "ron" 3 times.
I am trying to find, if a name is repeating twice, i should be able to populate it only one time. How can i achieve this ?

I would suggest you to use dictionary whose keys will be the author name (or the field which you don't want to be repetitive) and values will be the lists of contributors. For example,
Dictionary<string, List<Contributor>> contributors
= new Dictionary<string, List<Contributor>>();
Contributor contributor = new Contributor("ron", /*other values*/);
if ( !contributors.ContainsKey(contributor.Name) )
contributors.Add(contributor.Name,new List<Contributor>());
contributors[contributor.Name].Add(contributor);

Depending on your setup, I'd either use a StringCollection and just check if the name exists prior to insertion or just add all the names to a List and call Distinct() (extension method in System.Linq). So either:
StringCollection Names=new StringCollection();
if(!Names.Contains(Name))
Names.Add(Name);
As CharithJ suggests, or:
List<string> Names=new List<string>();
Names.Add(Name);
...
foreach(string Name in Names.Distinct())
{
...
}
Either would work well enough.

Use C#s .Contains() function to check if the name has already been added to the string

Create a model of what you want to achieve (it's like a view model) that drives the rendering of your "report". Then, the model can control this requirement of "only output each name once". Pseudo-code follows:
var ron = new Author("ron");
var ronnie = new Author("ronnie");
var report = new HistoryReport();
report.AddVersion(1, ron);
report.AddVersion(2, ron);
report.AddVersion(3, ronnie);
string renderedReport = report.Render();
// output e.g.:
// Versions 1 and 2 by ron; Version 3 by ronnie
Then you use that ouput to populate your literal control.
If you use simple string substitution, you'll mix up ron with ronnie.

StringCollection authors= new StringCollection();
if (!authors.Contains("Ron"))
{
authors.Add("Ron");
}

Related

How to do this kind of search in ASP.net MVC?

I have an ASP.NET MVC web application.
The SQL table has one column ProdNum and it contains data such as 4892-34-456-2311.
The user needs a form to search the database that includes this field.
The problem is that the user wants to have 4 separate fields in the UI razor view whereas each field should match with the 4 parts of data above between -.
For example ProdNum1, ProdNum2, ProdNum3 and ProdNum4 field should match with 4892, 34, 456, 2311.
Since the entire search form contains many fields including these 4 fields, the search logic is based on a predicate which is inherited from the PredicateBuilder class.
Something like this:
...other field to be filtered
if (!string.IsNullOrEmpty(ProdNum1) {
predicate = predicate.And(
t => t.ProdNum.toString().Split('-')[0].Contains(ProdNum1).ToList();
...other fields to be filtered
But the above code has run-time error:
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities`
Does anybody know how to resolve this issue?
Thanks a lot for all responses, finally, I found an easy way to resolve it.
instead of rebuilding models and change the database tables, I just add extra space in the search strings to match the search criteria. since the data format always is: 4892-34-456-2311, so I use Startwith(PODNum1) to search first field, and use Contains("-" + PODNum2 + "-") to search second and third strings (replace PODNum1 to PODNum3), and use EndWith("-" + PODNum4) to search 4th string. This way, I don't need to change anything else, it is simple.
Again, thanks a lot for all responses, much appreciated.
If i understand this correct,you have one column which u want to act like 4 different column ? This isn't worth it...For that,you need to Split each rows column data,create a class to handle the splitted data and finally use a `List .Thats a useless workaround.I rather suggest u to use 4 columns instead.
But if you still want to go with your existing applied method,you first need to Split as i mentioned earlier.For that,here's an example :
public void test()
{
SqlDataReader datareader = new SqlDataReader;
while (datareader.read)
{
string part1 = datareader(1).toString.Split("-")(0);///the 1st part of your column data
string part2 = datareader(1).toString.Split("-")(1);///the 2nd part of your column data
}
}
Now,as mentioned in the comments,you can rather a class to handle all the data.For example,let's call it mydata
public class mydata {
public string part1;
public string part2;
public string part3;
public string part4;
}
Now,within the While loop of the SqlDatareader,declare a new instance of this class and pass the values to it.An example :
public void test()
{
SqlDataReader datareader = new SqlDataReader;
while (datareader.read)
{
Mydata alldata = new Mydata;
alldata.Part1 = datareader(1).toString.Split("-")(0);
alldata.Part2 = datareader(1).toString.Split("-")(1);
}
}
Create a list of the class in class-level
public class MyForm
{
List<MyData> storedData = new List<MyData>;
}
Within the while loop of the SqlDatareader,add this at the end :
storedData.Add(allData);
So finally, u have a list of all the splitted data..So write your filtering logic easily :)
As already mentioned in a comment, the error means that accessing data via index (see [0]) is not supported when translating your expression to SQL. Split('-') is also not supported hence you have to resort to the supported functions Substring() and IndexOf(startIndex).
You could do something like the following to first transform the string into 4 number strings ...
.Select(t => new {
t.ProdNum,
FirstNumber = t.ProdNum.Substring(0, t.ProdNum.IndexOf("-")),
Remainder = t.ProdNum.Substring(t.ProdNum.IndexOf("-") + 1)
})
.Select(t => new {
t.ProdNum,
t.FirstNumber,
SecondNumber = t.Remainder.Substring(0, t.Remainder.IndexOf("-")),
Remainder = t.Remainder.Substring(t.Remainder.IndexOf("-") + 1)
})
.Select(t => new {
t.ProdNum,
t.FirstNumber,
t.SecondNumber,
ThirdNumber = t.Remainder.Substring(0, t.Remainder.IndexOf("-")),
FourthNumber = t.Remainder.Substring(t.Remainder.IndexOf("-") + 1)
})
... and then you could simply write something like
if (!string.IsNullOrEmpty(ProdNum3) {
predicate = predicate.And(
t => t.ThirdNumber.Contains(ProdNum3)

If string in list occurs in string, then add to list

had a look around and found many similar questions but none matching mine exactly.
public bool checkInvalid()
{
invalidMessage = filterWords.Any(s => appmessage.Contains(s));
return invalidMessage;
}
If a string is found that matches a string in the list the boolean invalidMessage is set to true.
After this though I would like to be able to add each string found to a list. is there a way I can do this using .Contains() or can someone recommend me another way to go about this?
Many thanks.
Well, from your description, I thought here is what you want:
// Set of filtered words
string[] filterWords = {"AAA", "BBB", "EEE"};
// The app message
string appMessage = "AAA CCC BBB DDD";
// The list contains filtered words from the app message
List<string> result = new List<string>();
// Normally, here is what you do
// 1. With each word in the filtered words set
foreach (string word in filterWords)
{
// Check if it exists in the app message
if (appMessage.Contains(word))
{
// If it does, add to the list
result.Add(word);
}
}
But as you said, you want to use LINQ, so instead of doing a loop, you can do it like this:
// If you want to use LINQ, here is the way
result.AddRange(filterWords.Where(word => appMessage.Contains(word)));
If what you want is to gets the words in filterWords that are contained in appmessage you can use Where:
var words = filterWords.Where(s => appmessage.Contains(s)).ToList();

Linq query, select everything from one lists property that starts with a string in another list

Hello I'm new to linq and lambda
I have two lists
fl.LocalOpenFiles ...
List<string> f....
there is a property (string) for example taking index 0
fl.LocalOpenFiles[0].Path
i wanted to select all from the first list fl.LocalOpenFiles where fl.LocalOpenFiles.Path starts with a string from the List<string> f
I finally got this...
List<LocalOpenFile> lof = new List<LocalOpenFile>();
lof = fl.LocalOpenFiles.Join(
folders,
first => first.Path,
second => second,
(first, second) => first)
.ToList();
But its just selecting folders that meet the requirement first.Path == second and i couldnt find a way to get the data that i want which is something meeting this "braindump" requirement:
f[<any>] == fl.LocalOpenFiles[<any>].Path.Substring(0, f[<any>].Length)
Another Example...
List<string> f = new List<string>{ "abc", "def" };
List<LocalOpenFile> lof = new List<LocalOpenFile>{
new LocalOpenFile("abc"),
new LocalOpenFile("abcc"),
new LocalOpenFile("abdd"),
new LocalOpenFile("defxsldf"),)}
// Result should be
// abc
// abcc
// defxsldf
I hope i explained it in a understandable way :)
Thank you for your help
Do you mean something like this :
List<LocalOpenFile> result =
lof.Where(file => f.Any(prefix => file.Path.StartsWith(prefix)))
.ToList();
You can use a regular where instead of a join, which will give you more straight forward control over the selection criteria;
var result =
from file in lof
from prefix in f
where file.Path.StartsWith(prefix)
select file.Path; // ...or just file if you want the LocalOpenFile objects
Note that a file matching multiple prefixes may show up more than once. If that is a problem, you can just add a call to Distinct to eliminate duplicates.
EDIT:
If you - as it seems in this case - only want to know the matching path and not the prefix it matches (ie you only want data from one collection as in this case), I'd go for #har07's Any solution instead.

LINQ to SharePoint 2010 getting error "All new entities within an object graph must be added/attached before changes are submitted."

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.
I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.
I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:
"All new entities within an object graph must be added/attached before changes are submitted."
this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.
With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.
I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,
I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.
Here is my code:
using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
{
SPSite sitecollection = new SPSite("http://dev");
SPWeb web = sitecollection.OpenWeb();
SPList theOldList = web.Lists.TryGetList("OldList_Physicians");
//Create new Physician entities.
foreach(SPListItem l in theOldList.Items)
{
PhysiciansItem p = new PhysiciansItem()
{
FirstName = (String)l["First Name"],
Title = (String)l["Last Name"],
MiddleInitial = (String)l["Middle Init"],
ProviderNumber = Convert.ToInt32(l["Provider No"]),
Gender = ConvertGender(l),
UndergraduateSchool =(String)l["UG_School"],
MedicalSchool = (String)l["Med_School"],
Residency = (String)l["Residency"],
Fellowship = (String)l["Fellowship"],
Internship = (String)l["Internship"],
PhysicianType = ConvertToPhysiciantype(l),
Specialty = ConvertSpecialties(l),
InsurancesAccepted = ConvertInsurance(l),
};
ctx.Physicians.InsertOnSubmit(p);
}
ctx.SubmitChanges(); //this is where it flakes out
}
}
//Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.
private Gender ConvertGender(SPListItem l)
{
Gender g = new Gender();
if ((String)l["Sex"] == "M")
{
g = Gender.M;
}
else g = Gender.F;
return g;
}
//Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and
//DO (Doctor of Osteopathic Medicine). State Regualtions require this information to be attached
//to a physician's profile.
private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
{
ProviderTypesItem p = new ProviderTypesItem();
p.Title = (String)l["Provider_Title:Title"];
p.Intials = (String)l["Provider_Title"];
return p;
}
//Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
private EntitySet<Item> ConvertSpecialties(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
Item i = new Item();
i.Title = (String)l["Provider Specialty"];
theEntityList.Add(i);
if ((String)l["Provider SubSpecialty"] != null)
{
Item theSubSpecialty = new Item();
theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
theEntityList.Add(theSubSpecialty);
}
return theEntityList;
}
//Process and add insurance accepted.
//Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
//list.
private EntitySet<Item> ConvertInsurance(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
if ((bool)l["TennCare"] == true)
{
Item TenncareItem = new Item();
TenncareItem.Title = "TennCare";
theEntityList.Add(TenncareItem);
}
if ((bool)l["Medicare"] == true)
{
Item MedicareItem = new Item();
MedicareItem.Title = "Medicare";
theEntityList.Add(MedicareItem);
}
if ((bool)l["Commercial"] == true)
{
Item CommercialItem = new Item();
CommercialItem.Title = "Commercial";
theEntityList.Add(CommercialItem);
}
return theEntityList;
}
}
So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).
You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)
Generate the entity for just that one list (Physicians) in some temporary folder
Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
Paste the lookup id & value properties in this partial Physicians class.
Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:
PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.
It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

Can we assign more then one template ID in templateId (like we do in relatedId), while searching with Lucene?

I have Five check boxes
Search All
Template 1
Template 2
Template 3
Template 4
If user selects Search All, then simply we can pass index name and get result, if user selects one of template specific check box, again simply we can do by passing template name, but if any of two templates specific check box(or may be three) are checked, then?
Can we pipe-separate templateIDs?
You may need to change the method in the Advanced Database Crawler to handle the GUIDs of templates passed in. The SearchParam class has a property called TemplateIds which by being plural indicates it should allow more than one. However, if you look into the code, this is how it is used:
templateIds = IdHelper.NormalizeGuid(templateIds);
The NormalizeGuid() method actually only returns a single GUID. So I recommend you alter the ApplyTemplateFilter() method here to return multiple Template GUIDs:
protected void ApplyTemplateFilter(CombinedQuery query, string templateIds, QueryOccurance occurance)
{
if (String.IsNullOrEmpty(templateIds)) return;
templateIds = IdHelper.NormalizeGuid(templateIds);
var fieldQuery = new FieldQuery(BuiltinFields.Template, templateIds);
query.Add(fieldQuery, occurance);
}
So change templateIds = IdHelper.NormalizeGuid(templateIds) to handle multiple GUIDs, perhaps by splitting the input at a | to get each GUID then normalizing each one of those and combining them again via a |.
Further to what Mark said this can be achieved by using below function:
protected void ApplyTemplateFilter(CombinedQuery query, string templateIds)
{
if (String.IsNullOrEmpty(templateIds)) return;
var fieldQuery = new CombinedQuery();
var values = IdHelper.ParseId(templateIds);
foreach (var value in values.Where(ID.IsID))
{
AddFieldValueClause(fieldQuery, BuiltinFields.Template, value, QueryOccurance.Should);
}
query.Add(fieldQuery, QueryOccurance.Must);
}

Categories