The first List AStore iterates and creates a new list for each iteration. The second List APages iterates, but does not create a new list on each iteration. I have the same placement for each list creation and List.Add. What is wrong here?
public void Promos()
{
//get store info and id
var storeinfo = new HtmlWeb();
var storeshtm = storeinfo.Load(#"Stores.htm");
var nodes = storeshtm.DocumentNode.SelectNodes("div");
List<Store> AStore = new List<Store>();
nodes = nodes[0].ChildNodes;
int a = 0;
foreach (var node in nodes)
{
if (node.Name != "#text")
{
AStore.Add(new Store(
node.ChildNodes[1].ChildNodes[1].Attributes[7].Value,//storewebid
"Astore",//storename
node.ChildNodes[3].ChildNodes[1].ChildNodes[1].ChildNodes[3].InnerText,//storeaddress
node.ChildNodes[3].ChildNodes[1].ChildNodes[1].ChildNodes[5].InnerText,//storecity
node.ChildNodes[3].ChildNodes[1].ChildNodes[1].ChildNodes[5].InnerText,//storestate
node.ChildNodes[3].ChildNodes[1].ChildNodes[1].ChildNodes[5].InnerText,//storezip
node.ChildNodes[3].ChildNodes[1].ChildNodes[1].ChildNodes[7].InnerText,//storephone
""//storehours
));
}
}
for (int i = 0; i <= a; i++)
{
var circualr = new HtmlWeb();
var storehtm = circualr.Load(#"http://storewebsite/" + AStore[i].StoreWebID);
var cnodes = storehtm.DocumentNode.SelectNodes("//*[#id="+'"'+"Wrapper"+'"'+"]");
List<Pages> APages = new List<Pages>();
foreach (var cnode in cnodes)
if(cnode.ChildNodes[3].ChildNodes[3].ChildNodes[5].ChildNodes[3].ChildNodes[1].Name == "a")
APages.Add(new Pages(cnode.ChildNodes[3].ChildNodes[3].ChildNodes[5].ChildNodes[3].ChildNodes[1].Attributes[2].Value));//get inner page links
}
I have the same placement for each list creation
You do NOT have the same placement of creation, the AStore is created outside of the for loop, and the APages one is.
Related
I have 2 PowerPoints one original and one edited. I each PowerPoint I have a textbox that contains a number of words. The idea is to find any words added or deleted from the original PowerPoint in the edited and put them in a list. I then want to put a strike though these words that have been added or deleted but I can't access any of the properties I need as I have a string and not a TextFrame. Any ideas how I could strike through the words? E.g original "This Text" edited "This New Text" outcome "This New Text".
My code is as fallows
List<int> originalShapesListID = new List<int>();
List<int> editedShapesListID = new List<int>();
List<int> originalListID = new List<int>();
List<int> editedListID = new List<int>();
List<Microsoft.Office.Interop.PowerPoint.Shape> originalList = new List<Microsoft.Office.Interop.PowerPoint.Shape>();
List<Microsoft.Office.Interop.PowerPoint.Shape> editList = new List<Microsoft.Office.Interop.PowerPoint.Shape>();
Microsoft.Office.Interop.PowerPoint.Shape editedShpID;
List<Microsoft.Office.Interop.PowerPoint.Shape> originalListWords = new List<Microsoft.Office.Interop.PowerPoint.Shape>();
List<char> editListWords = new List<char>();
editedShpID = null;
Microsoft.Office.Interop.PowerPoint.Shape originalShpID;
long editedShapeID;
long originalShapeID;
editedShapeID = 0;
originalShapeID = 0;
originalShpID = null;
originalShp = null;
editShp = null;
Microsoft.Office.Interop.PowerPoint.TextFrame txtFrame;
char delimiter = Convert.ToChar(" ");
List<string> one = new List<string>();
List<string> two = new List<string>();
int indexOfWord = 0;
Getting all Text
String pps = "";
foreach (Microsoft.Office.Interop.PowerPoint.Slide slide in Originalslides)
{
foreach (Microsoft.Office.Interop.PowerPoint.Shape originalShape in slide.Shapes)
{
originalShp = originalShape;
if (originalShape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textFrame = originalShape.TextFrame;
if (textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textRange = textFrame.TextRange;
pps += originalShape.TextFrame.TextRange.Text;
foreach (char word in pps)
{
l.Add(word);
Debug.WriteLine(word);
}
}
}
originalShapesListID.Add(originalShape.Id);
originalShapeID = originalShape.Id;
originalList.Add(originalShape);
}
originalListID.Add(slide.SlideID);
}
foreach (Microsoft.Office.Interop.PowerPoint.Slide slide in EditedSlides)
{
foreach (Microsoft.Office.Interop.PowerPoint.Shape editShape in slide.Shapes)
{
editShp = editShape;
if (editShape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textFrame = editShape.TextFrame;
if (textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textRange = textFrame.TextRange;
pps += editShape.TextFrame.TextRange.Text;
foreach (char word in pps)
{
l.Add(word);
Debug.WriteLine(word);
}
}
}
editedShapesListID.Add(editShape.Id);
editedShapeID = editShape.Id;
editList.Add(editShape);
}
editedListID.Add(slide.SlideID);
}
Checking if there is an added or deleted word
var q = from original in originalList
join editedTmp in editList on original.Id equals editedTmp.Id into g
from edited in g.DefaultIfEmpty()
select new
{
original,
edited
};
//foreach through both lists
foreach (var item in q)
{
List<string> diff;
var originalString = item.original.TextFrame.TextRange.Text;
var editString = item.edited.TextFrame.TextRange.Text;
var firstStringList = originalString.Split(delimiter).ToList();
var secondStringList = editString.Split(delimiter).ToList();
if (secondStringList.Count() > firstStringList.Count())
{
diff = secondStringList.Except(firstStringList).ToList();
//change to blue for added word
}
else
{
diff = firstStringList.Except(secondStringList).ToList();
// change to red for deleted word
}
}
The code below clicks on each button and then clicks on dynamic link produced by the button which takes me to next page however when test navigates back it fails to carry out the similar steps because of "Element not found in the cache - perhaps the page has changed since it was looked up". I need to test links and move backwards and forwards during the process.
Code to click and navigate
public static int ClickNestedLink(int ID, IList<string>allNestedlinks,IWebDriver _driver,string Url)
{
//Find the product brand and click on its brand and click on product link
foreach (string BrandName in allNestedlinks)
{
IList<string> ProductBrandButtonList = new List<string>();
//Find different brands
foreach (IWebElement ProductBrandButton in _driver.FindElements(By.ClassName("productSet")))
{
IWebElement ProductParent = ProductBrandButton.FindElement(By.ClassName("product-parent"));
String test = ProductParent.GetAttribute("data-product-id");
ProductBrandButtonList.Add(test);
//Find links for a brand
IList<string> LinksCollection = new List<string>();
foreach (IWebElement ImageLink in ProductParent.FindElements(By.ClassName("brand_image")))
{
String ImageLike2 = ImageLink.GetAttribute("src");
LinksCollection.Add(ImageLike2);
ImageLink.Click();
IWebElement productBrandClick = ProductParent.FindElement(By.ClassName("product-brand"));
String Test2 = productBrandClick.Text;
productBrandClick.Click();
_driver.Navigate().Back();
Task.Delay(20000).Wait(); }
}
}
return (ID);
}
The Click and Back actions are loading a new page with new elements making the previous references obsolete.
To overcome this issue, you need locate the elements in each loop:
// iterate the products
for (int i = 0; ; i++) {
var products = _driver.FindElements(By.CssSelector(".productSet .product-parent"));
if (i >= products.Count)
break;
// get the product for the iteration
var product = products[i];
// iterate the links
for (int j = 0; ; j++) {
var links = _driver.FindElements(By.CssSelector(".productSet .product-parent .brand_image"));
var brands = _driver.FindElements(By.CssSelector(".productSet .product-parent .product-brand"));
if (j >= links.Count)
break;
// get the link and brand for the iteration
var link = links[j];
var brand = brands[j];
// click and navigate back
}
}
Solution
public static int ClickNestedLink2(int ID, IList allNestedlinks, IWebDriver _driver, string Url)
{
var products = _driver.FindElements(By.CssSelector(".productSet .product-parent"));
var productCount = products.Count;
for (int i = 0; i < productCount; i++)
{
var product = _driver.FindElements(By.CssSelector(".productSet .product-parent"))[i];
var brandButtonsCount = product.FindElements(By.CssSelector(".productSet .product-parent .brand_image")).Count;
for (int btnIdx = 0; btnIdx < brandButtonsCount; btnIdx++)
{
product = _driver.FindElements(By.CssSelector(".productSet .product-parent"))[i];
var brandButton = product.FindElements(By.CssSelector(".productSet .product-parent .brand_image"))[btnIdx];
brandButton.Click();
var brandNameAnchor = product.FindElement(By.CssSelector(".productSet .product-parent .product-brand"));
brandNameAnchor.Click();
_driver.Navigate().Back();
OpenQA.Selenium.Support.UI.WebDriverWait wait = new WebDriverWait(_driver, new TimeSpan(0, 0, 10));
wait.Until(driver1 => ((IJavaScriptExecutor)_driver).ExecuteScript("return document.readyState").Equals("complete"));
}
}
return (ID = 0);
}
}
I previously had a nested for loop, like the following:
for(int i = 0; i < outerObject.Count(); i++)
{
for(int j = 0; j < outerObject[i].innerObject.Count(); j++)
{
DataRow myDataRow = myDataTable.NewRow();
myDataRow["Column1"] = outerObject[i].PropertyX;
myDataRow["Column2"] = innerObject[j].PropertyY;
myDataTable.Rows.Add(myDataRow);
}
}
I have refactored the above using the LINQ SelectMany operator, but I am unsure how to access the value that I was previously accessing using outerObject[i].PropertyX
var objects = outerObject.SelectMany(x => x.innerObject)
foreach (var object in objects)
{
DataRow myDataRow = myDataTable.NewRow();
//myDataRow["Column1"] = // I am unsure how to get the value of outerObject[i].PropertyX here
myDataRow["Column2"] = object.PropertyY;
myDataTable.Rows.Add(myDataRow);
}
Could someone please help me out here?
var objects = outerObject.SelectMany(x => new { outer = x, inner = x.innerObject })
foreach (var obj in objects)
{
DataRow myDataRow = myDataTable.NewRow();
myDataRow["Column1"] = obj.outer.PropertyX;
myDataRow["Column2"] = obj.inner.PropertyY;
myDataTable.Rows.Add(myDataRow);
}
In order to access the outer iterator, you can do at least two things.
Perform your loop logic within the lambda by giving it { } syntax or making it a full method
var objects = outerObject.SelectMany(x =>
{
var result = x.InnerObject;
DataRow myDataRow = myDataTable.NewRow();
myDataRow["Column1"] = x.PropertyX;
myDataRow["Column2"] = result.PropertyY;
myDataTable.Rows.Add(myDataRow);
return result;
}
Cache the iterator using new {...} syntax, since nested lambdas have the same access as nested loops.
var objects = outerObject.SelectMany(x => x.InnerObject
.Select(inner => new { Outer = x, Inner = inner } ));
foreach (var obj in objects)
{
DataRow myDataRow = myDataTable.NewRow();
myDataRow["Column1"] = obj.Outer.PropertyX;
myDataRow["Column2"] = obj.Inner.PropertyY;
myDataTable.Rows.Add(myDataRow);
}
My program should renumber footnotes in a word file.
We have a VBA-Macro, that does the same but it´s too slow.
Dim fussnote As Footnote
For Each fussnote In ActiveDocument.Footnotes
fussnote.Reference.Select
With Selection
With .FootnoteOptions
.Location = wdBottomOfPage
.NumberingRule = wdRestartContinuous
.StartingNumber = 1
.NumberStyle = wdNoteNumberStyleArabic
.NumberingRule = wdRestartSection
End With
.Footnotes.Add range:=Selection.range, Reference:=""
End With
Next
I tried to clone all footnotes, then go through them (and their references) and change their ID (is Id the right property?)
My Code looks like this (not working):
public override void Work(WordprocessingDocument args)
{
var __allFootnotes = (Footnotes)args.MainDocumentPart
.FootnotesPart.Footnotes.Clone();
var footnotes = __allFootnotes.Elements<Footnote>()
.SkipWhile(f => !(f.Id.Value > 0)).ToList();
RenumberFootnotes(footnotes,
args.MainDocumentPart.Document.Body.Descendants<Paragraph>().ToList());
var __styles = args.MainDocumentPart
.StyleDefinitionsPart.Styles;
for (int i = 0; i < footnotes.Count(); i++)
{
//var footnote = footnotes[i];
}
args.MainDocumentPart.FootnotesPart
.Footnotes = __allFootnotes;
}
private void RenumberFootnotes(List<Footnote> footnotes, List<Paragraph> paragraphs)
{
var __p = paragraphs.Where(p => p.Descendants<FootnoteReference>().Any());
var __references = __p.SelectMany(p => p.Descendants<FootnoteReference>());
for (int i = 1; i < footnotes.Count; i++)
{
var __tempId = footnotes[i].Id.Value;
footnotes[i].Id.Value = i;
var __reference = __references.First(fr => fr.Id.Value == __tempId);
__reference.Id.Value = i;
}
}
Solution extracted from question:
You have to add a new 'FootnoteReferenceMark' Object into a Run in your Footnote.
'footnote' is my variable for a footnote. Then i just take the first descendant of type 'Run' and append a new child of 'FootnoteReferenceMark'.
footnote.Descendants<Run>().First().AppendChild(new FootnoteReferenceMark());
I am using the follow Code to read an XML file:
XDocument doc = XDocument.Load(xmlPath);
foreach (var node in doc.Descendants("LogInfo"))
{
ListViewItem item = new ListViewItem(new string[]
{
node.Element("MailBox").Value,
node.Element("LastRun").Value,
});
listViewHome.Items.Add(item);
}
How can I change this Code to receive just the last "n" number of elements from that XML file?
Thanks in advance.
You could do this:
var nodes = doc.Descendants("LogInfo");
foreach (var node in nodes.Skip(Items.Count() - n))
{
...
}
Or this:
var nodes = doc.Descendants("LogInfo");
foreach (var node in nodes.Reverse().Take(n).Reverse())
{
...
}
If you're feeling adventurous, you could also write your own extension method which should be more efficient than either of these. Here's my quick and dirty solution:
public static IEnumerable<T> TakeFromEnd<T>(this IEnumerable<T> items, int n)
{
var arry = new T[n];
int i = 0;
foreach(var x in items)
{
arry[i++ % n] = x;
}
if (i < n)
{
n = i;
i = 0;
}
for (int j = 0; j < n; j++)
{
yield return arry[(i + j) % n];
}
}
var nodes = doc.Descendants("LogInfo");
foreach (var node in nodes.TakeFromEnd(n))
{
...
}
XDocument doc = XDocument.Load(xmlPath);
var logs = doc.Descendants("LogInfo");
var logsCount = logs.Count();
foreach (var node in logs.Skip(logsCount - n).Take(n))
{
ListViewItem item = new ListViewItem(new string[]
{
node.Element("MailBox").Value,
node.Element("LastRun").Value,
});
listViewHome.Items.Add(item);
}
And here is XPath solution, which will enumerate xml once
var xpath = String.Format("//LogInfo[position()>last()-{0}]", n);
foreach (var log in doc.XPathSelectElements(xpath))
{
ListViewItem item = new ListViewItem(new string[]
{
(string)log.Element("MailBox"),
(string)log.Element("LastRun")
});
listViewHome.Items.Add(item);
}