I am trying to find out the duplicate Elements in XElement , and make a generic function to remove duplicates .Something like:
public List<Xelement>RemoveDuplicatesFromXml(List<Xelement> xele)
{ // pass the Xelement List in the Argument and get the List back , after deleting the duplicate entries.
return xele;
}
the xml is as follows:
<Execute ID="7300" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7301" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7302" Attrib1="xyz1" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
I want get duplicates on every attribute excluding ID ,and then delete the one having lesser ID.
Thanks,
You can implement custom IEqualityComparer for this task
class XComparer : IEqualityComparer<XElement>
{
public IList<string> _exceptions;
public XComparer(params string[] exceptions)
{
_exceptions = new List<string>(exceptions);
}
public bool Equals(XElement a, XElement b)
{
var attA = a.Attributes().ToList();
var attB = b.Attributes().ToList();
var setA = AttributeNames(attA);
var setB = AttributeNames(attB);
if (!setA.SetEquals(setB))
{
return false;
}
foreach (var e in setA)
{
var xa = attA.First(x => x.Name.LocalName == e);
var xb = attB.First(x => x.Name.LocalName == e);
if (xa.Value == null && xb.Value == null)
continue;
if (xa.Value == null || xb.Value == null)
return false;
if (!xa.Value.Equals(xb.Value))
{
return false;
}
}
return true;
}
private HashSet<string> AttributeNames(IList<XAttribute> e)
{
return new HashSet<string>(e.Select(x =>x.Name.LocalName).Except(_exceptions));
}
public int GetHashCode(XElement e)
{
var h = 0;
var atts = e.Attributes().ToList();
var names = AttributeNames(atts);
foreach (var a in names)
{
var xa = atts.First(x => x.Name.LocalName == a);
if (xa.Value != null)
{
h = h ^ xa.Value.GetHashCode();
}
}
return h;
}
}
Usage:
var comp = new XComparer("ID");
var distXEle = xele.Distinct(comp);
Please note that IEqualityComparer implementation in this answer only compare LocalName and doesn't take namespace into considerataion. If you have element with duplicate local name attribute, then this implementation will take the first one.
You can see the demo here : https://dotnetfiddle.net/w2DteS
Edit
If you want to
delete the one having lesser ID
It means you want the largest ID, then you can chain the .Distinct call with .Select.
var comp = new XComparer("ID");
var distXEle = xele
.Distinct(comp)
.Select(z => xele
.Where(a => comp.Equals(z, a))
.OrderByDescending(a => int.Parse(a.Attribute("ID").Value))
.First()
);
It will guarantee that you get the element with largest ID.
Use Linq GroupBy
var doc = XDocument.Parse(yourXmlString);
var groups = doc.Root
.Elements()
.GroupBy(element => new
{
Attrib1 = element.Attribute("Attrib1").Value,
Attrib2 = element.Attribute("Attrib2").Value,
Attrib3 = element.Attribute("Attrib3").Value,
Attrib4 = element.Attribute("Attrib4").Value,
Attrib5 = element.Attribute("Attrib5").Value
});
var duplicates = group1.SelectMany(group =>
{
if(group.Count() == 1) // remove this if you want only duplicates
{
return group;
}
int minId = group.Min(element => int.Parse(element.Attribute("ID").Value));
return group.Where(element => int.Parse(element.Attribute("ID").Value) > minId);
});
Solution above will remove elements with lesser ID which have duplicates by attributes.
If you want return only elements which have duplicates then remove if fork from last lambda
I've tried to search SO for solutions and questions that could be similar to my case.
I got 2 collections of objects:
public class BRSDocument
{
public string IdentifierValue { get; set;}
}
public class BRSMetadata
{
public string Value { get; set;}
}
I fill the list from my datalayer:
List<BRSDocument> colBRSDocuments = Common.Instance.GetBRSDocuments();
List<BRSMetadata> colBRSMetadata = Common.Instance.GetMessageBRSMetadata();
I now want to find that one object in colBRSDocuments where x.IdentifierValue is equal to the one object in colBRSMetadata y.Value. I just need to find the BRSDocument that matches a value from the BRSMetadata objects.
I used a ordinary foreach loop and a simple linq search to find the data and break when the value is found. I'm wondering if the search can be done completely with linq?
foreach (var item in colBRSMetadata)
{
BRSDocument res = colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value);
if (res != null)
{
//Do work
break;
}
}
Hope that some of you guys can push me in the right direction...
Why not do a join?
var docs = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select d;
If there's only meant to be one then you can do:
var doc = docs.Single(); // will throw if there is not exactly one element
If you want to return both objects, then you can do the following:
var docsAndData = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select new
{
Doc = d,
Data = m
};
then you can access like:
foreach (var dd in docsAndData)
{
// dd.Doc
// dd.Data
}
Use Linq ?
Something like this should do the job :
foreach (var res in colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).Where(res => res != null))
{
//Do work
break;
}
If you are just interested by the first item, then the code would be :
var brsDocument = colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).FirstOrDefault(res => res != null);
if (brsDocument != null)
//Do Stuff
private ElementDefinition ParseComplexType(XElement complexType, string nameValue = "")
{
var name = complexType.Attribute("name");
ElementDefinition element = new ElementDefinition()
{
Elements = new List<ElementDefinition>(),
ElementName = name != null ? name.Value : string.Empty
};
foreach (var el in complexType.Descendants().Where(k => k.Parent.Parent == complexType && k.Name.LocalName == "element"))
{
ElementDefinition tempElement = new ElementDefinition();
var tempName = el.Attribute("name");
var tempType = el.Attribute("type");
if (tempName != null)
{
tempElement.ElementName = tempName.Value;
}
if (tempType != null)
{
var tempTypeValue = tempType.Value.Substring(tempType.Value.IndexOf(":") + 1, tempType.Value.Length - tempType.Value.IndexOf(":") - 1);
if (tipovi.Contains(tempTypeValue))
{
tempElement.ElementType = tempTypeValue;
element.Elements.Add(tempElement);
}
else
{
complexType = GetComplexType(tempTypeValue);
element.Elements.Add(ParseComplexType(complexType, tempName.Value));
}
}
}
if (nameValue != "") element.ElementName = nameValue;
return element;
}
Hi so this is a function i use for parsing XSD complexTypes.
This is a xsd schema i use xsd Schema.
I have problem parsing complexType element at line 14.
It only parses shipTo element, skipping billTo and parsing badly items.
The result is http://pokit.org/get/?b335243094f635f129a8bc74571c8bf2.jpg
Which fixes can i apply to this function in order to work properly?
PS. "tipovi" is list of xsd supported types, e.g. string, positiveInteger....
EDITED:
private XElement GetComplexType(string typeName)
{
XNamespace ns = "http://www.w3.org/2001/XMLSchema";
string x = "";
foreach (XElement ele in xsdSchema.Descendants())
{
if (ele.Name.LocalName == "complexType" && ele.Attribute("name") != null)
{
x = ele.Attribute("name").Value;
if (x == typeName)
{
return ele;
}
}
}
return null;
}
GetComplexType finds complexType definition of an element type. For example, for "PurchaseOrderType" (line 10) it returns element at line 14.
NOTE: This is only a partial answer as it only explains the issue regarding the skipped "billTo" element. The code as presented in the question has many more issues.
The problem regarding skipping of the billTo element
The complexType variable is used in the predicate for the Linq method Where in the foreach loop:
complexType.Descendants().Where(k => k.Parent.Parent == complexType && k.Name.LocalName == "element"))
This lambda expression uses the variable complexType, not merely its value.
By assigning another value to complexType deep down inside your foreach loop
complexType = GetComplexType(tempTypeValue);
you also change the logic of which elements are filtered by the predicate of the Where method in the the foreach loop.
The Fix
The solution is rather simple: Do not change the complexType variable within the foreach loop. You could do the call of GetComplexType like this:
XElement complexTypeUsedByElement = GetComplexType(tempTypeValue);
element.Elements.Add(ParseComplexType(complexTypeUsedByElement, tempName.Value));
i got error as: An unhandled exception of type 'System.IndexOutOfRangeException' occurred in app.exe
Additional information: Index was outside the bounds of the array.
by using code below, i appreciate your help in-advance all.
public string getMissingFields(WebBrowser wb, DataRow dr)
{
string Available2 = "";
Available2 = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt")
.Style.Split(';')
.Where(x => x.Contains("display"))
.ToArray()[0].Split(':')[1];
string FieldsMissing="";
if( Available2 .Contains( "inline" )) {
FieldsMissing = FieldsMissing + "First name missing!" + ", ";
}
return FieldsMissing;
}
You're assumiing that the style will always contain "display", which apparently it does not. Replace your indexer call to offset 0 with a call to FirstOrDefault(), then test for null:
Available2 = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt").Style.Split(';').Where(x => x.Contains("display")).ToArray().FirstOrDefault();
if( null != Available2 )
{
// continue
}
Available2 = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt")
.Style.Split(';')
.Where(x => x.Contains("display"))
.ToArray()[0].Split(':')[1];
Two possible problems:
Either ToArray() does return an empty array, at with point accessing element 0 causes this error. Or it is at the point where you are accessing the element at index 1 - maybe there is no such element, because in the string you're trying to split there is no :? Debug your code or make sure that there is at least one element returned by ToArray() and two elements returned by Split.
You could try this now. This splits your code so that you can easily debug:
var items = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt")
.Style.Split(';')
.Where(x => x.Contains("display"))
.ToArray();
if (items.Count > 0)
{
string[] split = items[0].Split(':');
if (split.Length > 1)
Available2 = split[1];
}
Two possibilities:
ToArray() returning empty array.You are trying to an element which is not exist.
Split(':') returning zero or one element.
Debug your code and find which one is true.
It seems you don't need ToArray.Just use FirstOrDefault, and check returning result whether null or not.If it isn't null call Split and check again to prevent exception.
Available2 = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt")
.Style.Split(';')
.Where(x => x.Contains("display"))
.FirstOrDefault();
if(Available2 != null)
{
var text = Available2.Split(':');
if(text.Length > 1)
{
var result = text[1];
}
}
First, consider that there's no control with id=ContentPlaceHolder1_reqTxt:
var reqTxt = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt");
You have to handle the case that it's null:
if(reqTxt != null)
{
}
Now consider that there's is no style display, then ToArray returns an empty array. You can use FirstOrDefault and check for null gain:
string Available2 = null;
if(reqTxt != null)
{
var firstDisplayStyle = reqTxt.Split(';')
.FirstOrDefault(s => s.Contains("display"));
if(firstDisplayStyle != null)
{
string[] displaySplit = firstDisplayStyle.Split(':');
// now handle the case that there is no colon:
if(displaySplit.Length > 1)
Available2 = displaySplit[1];
}
}
Available2=wb.Document.GetElementById("ContentPlaceHolder1_reqTxt").Style.Split(';').Where(x => x.Contains("display")).ToArray()[0].Split(':')[1];
To find problem, decomposite to:
if (wb == null || wb.Document == null )
return;
var element = wb.Document.GetElementById("ContentPlaceHolder1_reqTxt");
if (element == null || element.Style == null)
return;
var displayItems = element.style.Split(';').Where(x=> x.Contains("display")).FirstOrDefault();
if ( displayItems == null)
return;
var colonItems = displayItems.Split(':');
if ( colonItems.Count() < 2 )
return;
var Available2 = colonItems.Skip(1).First();
Here I have a simple example to find an item in a list of strings. Normally I use a for loop or anonymous delegate to do it like this:
int GetItemIndex(string search)
{
int found = -1;
if ( _list != null )
{
foreach (string item in _list) // _list is an instance of List<string>
{
found++;
if ( string.Equals(search, item) )
{
break;
}
}
/* Use an anonymous delegate
string foundItem = _list.Find( delegate(string item) {
found++;
return string.Equals(search, item);
});
*/
}
return found;
}
LINQ is new for me. Can I use LINQ to find an item in the list? If it is possible, how?
There are a few ways (note that this is not a complete list).
Single will return a single result, but will throw an exception if it finds none or more than one (which may or may not be what you want):
string search = "lookforme";
List<string> myList = new List<string>();
string result = myList.Single(s => s == search);
Note that SingleOrDefault() will behave the same, except it will return null for reference types, or the default value for value types, instead of throwing an exception.
Where will return all items which match your criteria, so you may get an IEnumerable<string> with one element:
IEnumerable<string> results = myList.Where(s => s == search);
First will return the first item which matches your criteria:
string result = myList.First(s => s == search);
Note that FirstOrDefault() will behave the same, except it will return null for reference types, or the default value for value types, instead of throwing an exception.
If you want the index of the element, this will do it:
int index = list.Select((item, i) => new { Item = item, Index = i })
.First(x => x.Item == search).Index;
// or
var tagged = list.Select((item, i) => new { Item = item, Index = i });
int index = (from pair in tagged
where pair.Item == search
select pair.Index).First();
You can't get rid of the lambda in the first pass.
Note that this will throw if the item doesn't exist. This solves the problem by resorting to nullable ints:
var tagged = list.Select((item, i) => new { Item = item, Index = (int?)i });
int? index = (from pair in tagged
where pair.Item == search
select pair.Index).FirstOrDefault();
If you want the item:
// Throws if not found
var item = list.First(item => item == search);
// or
var item = (from item in list
where item == search
select item).First();
// Null if not found
var item = list.FirstOrDefault(item => item == search);
// or
var item = (from item in list
where item == search
select item).FirstOrDefault();
If you want to count the number of items that match:
int count = list.Count(item => item == search);
// or
int count = (from item in list
where item == search
select item).Count();
If you want all the items that match:
var items = list.Where(item => item == search);
// or
var items = from item in list
where item == search
select item;
And don't forget to check the list for null in any of these cases.
Or use (list ?? Enumerable.Empty<string>()) instead of list.
Do you want the item in the list or the actual item itself (would assume the item itself).
Here are a bunch of options for you:
string result = _list.First(s => s == search);
string result = (from s in _list
where s == search
select s).Single();
string result = _list.Find(search);
int result = _list.IndexOf(search);
If it really is a List<string> you don't need LINQ, just use:
int GetItemIndex(string search)
{
return _list == null ? -1 : _list.IndexOf(search);
}
If you are looking for the item itself, try:
string GetItem(string search)
{
return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
}
This method is easier and safer
var lOrders = new List<string>();
bool insertOrderNew = lOrders.Find(r => r == "1234") == null ? true : false
How about IndexOf?
Searches for the specified object and returns the index of the first occurrence within the list
For example
> var boys = new List<string>{"Harry", "Ron", "Neville"};
> boys.IndexOf("Neville")
2
> boys[2] == "Neville"
True
Note that it returns -1 if the value doesn't occur in the list
> boys.IndexOf("Hermione")
-1
This will help you in getting the first or default value in your LINQ List search
var results = _List.Where(item => item == search).FirstOrDefault();
This search will find the first or default value, which it will return.
I used to use a Dictionary which is some sort of an indexed list which will give me exactly what I want when I want it.
Dictionary<string, int> margins = new Dictionary<string, int>();
margins.Add("left", 10);
margins.Add("right", 10);
margins.Add("top", 20);
margins.Add("bottom", 30);
Whenever I wish to access my margins values, for instance, I address my dictionary:
int xStartPos = margins["left"];
int xLimitPos = margins["right"];
int yStartPos = margins["top"];
int yLimitPos = margins["bottom"];
So, depending on what you're doing, a dictionary can be useful.
If we need to find an element from the list, then we can use the Find and FindAll extensions method, but there is a slight difference between them. Here is an example.
List<int> items = new List<int>() { 10, 9, 8, 4, 8, 7, 8 };
// It will return only one 8 as Find returns only the first occurrence of matched elements.
var result = items.Find(ls => ls == 8);
// this will returns three {8,8,8} as FindAll returns all the matched elements.
var result1 = items.FindAll(ls => ls == 8);
Here is one way to rewrite your method to use LINQ:
public static int GetItemIndex(string search)
{
List<string> _list = new List<string>() { "one", "two", "three" };
var result = _list.Select((Value, Index) => new { Value, Index })
.SingleOrDefault(l => l.Value == search);
return result == null ? -1 : result.Index;
}
Thus, calling it with
GetItemIndex("two") will return 1,
and
GetItemIndex("notthere") will return -1.
Reference: linqsamples.com
Try this code:
return context.EntitytableName.AsEnumerable().Find(p => p.LoginID.Equals(loginID) && p.Password.Equals(password)).Select(p => new ModelTableName{ FirstName = p.FirstName, UserID = p.UserID });
You can use FirstOfDefault with the Where LINQ extension to get a MessageAction class from the IEnumerable. Reme
var action = Message.Actions.Where(e => e.targetByName == className).FirstOrDefault<MessageAction>();
where
List<MessageAction> Actions { get; set; }
One more way to check the existence of an element in a List<string>:
var result = myList.Exists(users => users.Equals("Vijai"))
You want to search an object in object list.
This will help you in getting the first or default value in your Linq List search.
var item = list.FirstOrDefault(items => items.Reference == ent.BackToBackExternalReferenceId);
or
var item = (from items in list
where items.Reference == ent.BackToBackExternalReferenceId
select items).FirstOrDefault();