I am facing an issue where I have to drill down through the list till I find the desired contentId. Once the contentId is matched, I need to get its ParentIds. I am able to get the contentId but not its ParentId.
Currently I am using recursion to get the ParentIds of a child node. But failed to get the desired results.
Can anyone make it run, or provide the correct code to get this issue fixed. I'm trying to get the childnode and its parentIds. I need to get the parentIds and then want to insert into a List.
I'm able to drill down into the loop but don't know how and when to store the parentIds into a list.
In this code, I am trying to get the parents of contentId "5".
class Program
{
static void Main(string[] args)
{
Program obj = new Program();
var data = obj.GetAllChildCats();
foreach (var item in data)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
public List<int> GetAllChildCats()
{
var ret = getdata();
var data = GetAllChildCats(4, ret.contentObjects, 0);
return data;
}
List<int> parentIdsList1 = new List<int>();
private List<int> GetAllChildCats(int id, ContentObjects data, int Parentid)
{
if (!string.IsNullOrEmpty(data.ContentObjectId.ToString()))
{
parentIdsList1.Add(Parentid);
if (data.ContentObjectId == id)
{
return parentIdsList1;
}
else
{
if (data.ChildContentObjects != null)
{
foreach (ContentObjects cat in data.ChildContentObjects)
{
GetAllChildCats(id, cat, data.ContentObjectId);
}
}
}
}
return parentIdsList1;
}
public Heirarchy getdata()
{
Heirarchy ret = new Heirarchy()
{
_id = 11,
contentObjects = new ContentObjects()
{
ContentObjectId = 1,
NodeId = 34,
ChildContentObjects = new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=2,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=3,
NodeId=34,
ChildContentObjects= null
}
}
},
new ContentObjects() {
ContentObjectId=4,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=5,
NodeId=34,
ChildContentObjects= null
}
}
},
}
},
HierarchyId = 2
};
return ret;
}
}
public class Heirarchy
{
public int _id { get; set; }
public ContentObjects contentObjects { get; set; }
public int HierarchyId { get; set; }
}
public class ContentObjects
{
public int ContentObjectId { get; set; }
public int NodeId { get; set; }
public List<ContentObjects> ChildContentObjects { get; set; }
}
In this code, I am trying to get the parents of contentId "5".
This can be solved by a simple depth-first search. We just have to check the children's ID before digging deeper into the tree, so that we can still return the parent reference:
static void Main(string[] args)
{
Program obj = new Program();
var parents = obj.GetParentsOf(5, obj.getdata().contentObjects);
Console.WriteLine(parents.Count()); // yields 1
Console.WriteLine(parents.First().ContentObjectId); // yields 4
Console.ReadLine();
}
private IEnumerable<ContentObjects> GetParentsOf(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
// If a direct child has the requested ID, we are a parent.
if (c.ContentObjectId == id)
{
yield return root;
}
// Recurse deeper down.
foreach (ContentObjects found in GetParentsOf(id, c))
{
yield return found;
}
}
}
}
If, on the other hand, by "parents" you mean the complete path down the tree, we need to modify the method as follows. We again have a recursive depth-first search, but we insert our own id into the path when returning from a successful recursive step:
static void Main(string[] args)
{
Program obj = new Program();
var path = obj.GetPathTo(5, obj.getdata().contentObjects);
// prints 1, 4
foreach (ContentObjects o in path)
{
Console.WriteLine(o.ContentObjectId);
}
Console.ReadLine();
}
// returns null if id could not be found
private IEnumerable<ContentObjects> GetPathTo(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
if (c.ContentObjectId == id)
{
// If a direct child has the requested ID, we are the first parent.
return new[] { root };
}
else
{
// Recurse deeper down.
var found = GetPathTo(id, c);
if (found != null)
{
// We found something deeper down. Since we are part of the
// path, append own id.
return new[] { root }.Concat(found);
}
}
}
}
return null;
}
Related
I have a nested menu - here a simplified class:
public class NestedNode
{
public string Url { get; set; }
public List<NestedNode> Children { get; set; }
}
Given that I have a recursive list of NestedNode, I'm trying to ascertain whether any descendant is active at any level.
Here's the code to test:
protected void Page_Load(object sender, EventArgs e)
{
// The url of the current page
var currentUrl = Request.Url.GetLeftPart(UriPartial.Path);
// This is a list of nested nodes
var nodes = SiloNodes;
// Start loop
RecursiveCall(nodes, currentUrl);
}
void RecursiveCall(IEnumerable<NestedNode> nodes, string currentUrl)
{
if (nodes == null) return;
foreach (var n in nodes)
{
// This can test current level only
//var isActive = n.Url == currentUrl;
// This can test next level down
//var isActive = n.Children.Any(c => c.Url == currentUrl);
// How can I test all levels in one go?
RecursiveCall(n.Children, currentUrl);
}
}
What I need to be able to do is work out if any of the parents children are active (at the top lavel) so that I can add classes. At the moment, my ideas only go one level deep.
How about something like
void Main()
{
var nodes = new List<NestedNode>();
var isActive = nodes.Any(n => n.AnyActive("url"));
}
public class NestedNode
{
public NestedNode()
{
Children = Enumerable.Empty<NestedNode>();
}
public string Url { get; set; }
public IEnumerable<NestedNode> Children { get; set; }
public bool AnyActive(string url){ return Url==url || Children.Any(c => c.AnyActive(url));}
}
In this situation I would probably add a method to the NestedNode to check the condition recursively - something like this:
public bool ExistsRecursive(Func<NestedNode, bool> predicate)
{
if(predicate(this))
{
return true;
}
foreach(var node in Children)
{
return predicate(node);
}
return false;
}
And then, in your Page_Load, all you need is this:
if(nodes.ExistsRecursive(n => n.Url == currentUrl))
{
// current url is found in at least one node
}
I am standing on a complex issue for me. I need update some models, but I like to work with a generic class not to rewrite some code individually.
I need to update data that have lists on their properties, with possible exclusion or inclusion of items on these lists, but these lists can be of any other class / type. My questions are commented on the code.
These models are unrealistic and a bit absurds but have similarities with my real models, note that the logic is reversed on these relationships during updates.
Thanks for all.
public class RedNotebook
{
[Key]
public int Id { get; set; }
public string PageTitle { get; set; }
public virtual ICollection<Signature> Signatures { get; set; }
}
public class BlueNotebook
{
[Key]
public int Id { get; set; }
public DateTime Entrance { get; set; }
public DateTime Leave { get; set; }
public virtual ICollection<Guest> GuestList { get; set; }
}
public class Signature
{
[key]
public int Id { get; set; }
public string PeopleSignature { get; set; }
public int IdRedNotebook { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdRedNotebook")]
public virtual RedNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
public class Guest
{
[key]
public int Id { get; set; }
public string Name { get; set; }
public int SeatNumber { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
/**********************/
public void UpdateData(T newData, out string msg)
{
try
{
var propId = newData.GetType().GetProperty("Id");
if (propId == null)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
int id = Convert.ToInt32(propId.GetValue(newData));
if (id <= 0)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
//instance a determined DbContext and Model<T>
var contexto = new CtxCliente(DAO.Classes.Util.InstanciarConexao(strCripto, (DAO.Conectores) Conector));
var model = contexto.Set<T>();
var targetData = model.Find(id);
if (targetData == null)
{
model.Add(newData);
contexto.Entry(model).State = EntityState.Added;
msg = "An addition was made because there was no previous reference.";
}
if (Convert.ToInt32(targetData.GetType().GetProperty("Id").GetValue(targetData)) > 0)
{
contexto.Entry(targetData).CurrentValues.SetValues(newData);
contexto.Entry(targetData).State = EntityState.Modified;
msg = string.Empty;
}
//TODO - 1) GET THE VIRTUAL PROPERTIES OF WHICH TYPE targetData ICollection
//TODO - 2) COMPARE THE CONTENT OF VIRTUAL PROPERTIES OF targetData WITH THE CONTENTS OF VIRTUAL PROPERTIES UPDATE, BOTH ICollection
//TODO - 3) REMOVE EXCESS OF targetData AND / OR ADD THAT AS THE CASE MAY BE MISSING (A - CLEAR DIFFERENCE, B - ADD DIFFERENCE)
//through the properties to identify those that are of the collection type
foreach (var propertytargetData in targetData.GetType().GetProperties())
{
if (!propertytargetData.PropertyType.IsGenericType)
continue;
var propsNewData = newData.GetType().GetProperty(propertytargetData.Name);
#region
//if all list items were removed on update
if (propsNewData == null && propertytargetData != null)
{
// NOT TESTED, MAYBE NOT WORK CORRECTLY
propertytargetData.SetValue(targetData,null);
}
//If an item was included or removed
else if (propsNewData != null)
{
var valTargetData = propertytargetData.GetValue(targetData);
var valNewData = propsNewData.GetValue(newData);
var listItemsTargetData = (IEnumerable) valTargetData;
var listItemsNewData = (IEnumerable) valNewData;
int countItemsTargetData = listItemsTargetData.Cast<object>().Count();
int countItemsNewData = listItemsNewData.Cast<object>().Count();
if (countItemsTargetData > countItemsNewData) //remove discarded
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int) idItemtargetData == (int) iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
else if (countItemsTargetData < countItemsNewData) //Items need to be included
{
foreach (var newItem in listItemsNewData)
{
var idnewItem = newItem.GetType().GetProperty("Id").GetValue(newItem);
if ((int) idnewItem == 0)
{
//how to insert in list???????? (targetData)
}
else // remove and/or update some before (reduntant!?)
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int)idItemtargetData == (int)iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
}
}
}
}
contexto.SaveChanges(); //save data on model
}
catch(...){}
}
Haven't tested it . But it should work if both source and dest implement the same ICollection interface and T has an Id property of type System.Int32. It uses the new dynamic keyword that enables you to do duck typing ;
private class IdComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
//return ((dynamic) x).Id = ((dynamic) y).Id; //previous with convertion error
return ((dynamic) x).Id == ((dynamic) y).Id;
}
public int GetHashCode(object obj)
{
return ((dynamic) obj).Id;
}
}
private static void Copy(IEnumerable source, IEnumerable dest)
{
var cmp = new IdComparer();
var toRemove = dest.Cast<object>().Except(source.Cast<object>(),cmp).ToList();
var toAdd= source.Cast<object>().Except(dest.Cast<object>(),cmp).ToList();
foreach(var item in toAdd)
{
// dynamic runtime tries to find method that matches signiture void Add(T value so we add dummy variable so that it knows to search for bool Add(T value)
var dummy= ((dynamic) dest).Add(item);
}
foreach (var item in toRemove)
{
var dummy= ((dynamic)dest).Remove(item);
}
}
Let´s say I have two classes:
public Foo
{
public List<Foo> Childs { get; set; }
public Bar BarObj { get; set; }
public int Level { get; set; }
}
public Bar
{
public List<Foo> Childs { get; set; }
}
Now I want to get the nesting-level from a Collection of "Foo" Objects
my current working Method looks like this:
int currentLevel = 0;
public void ApplyNestingLevel(List<Foo> list)
{
foreach(var item in list)
{
item.Level = currentLevel;
if(item.Childs.Count > 0 || item.BarObj.Childs.Count > 0)
{
currentLevel++;
}
ApplyNestingLevel(item.Childs);
ApplyNestingLevel(item.BarObj.Childs);
}
}
how could I make this more "elegant/simple" ?
public void ApplyNestingLevel(Foo f)
{
ApplyNestingLevel(f, 0);
}
public void ApplyNestingLevel(Foo f, int level)
{
if(f == null) { return; }
f.Level = level
if(f.Childs != null) {
foreach(Foo child in f.Childs)
{
ApplyNestingLevel(child, level + 1);
}
}
if(f.BarObj != null && f.BarObj.Childs != null) {
foreach(Foo child in f.BarObj.Childs)
{
ApplyNestingLevel(child, level + 1);
}
}
}
Store a reference to the parent and make the Level property recursive.
I added an example and a couple other design suggestions in the code sample below. Hope this helps. FYI, this is pretty much straight out of the Gang of Four's design for the Composite Pattern, which should be required reading for anyone who is serious about OOP.
DoFactory .NET Composite Pattern
Design Patterns: Elements of Reusable Object-Oriented Software, on Amazon.com
public class Foo
{
public Foo(Foo parent = default(Foo))
{
this.parent = parent;
this.children = new List<Foo>();
}
private readonly Foo parent;
private readonly List<Foo> children;
public int Level { get { return ReferenceEquals(parent,null) ? 0 : parent.Level + 1; } }
// don't expose the actual list... see below for why
public IEnumerable<Foo> Children { get { foreach(Foo child in this.children) yield return child; } }
// instead of exposing the child object list
// declare an explicit method with any parameters
// necessary. this allows you to enforce the invariant
// condition that all objects in a children collection
// will have their parent reference set to their
// actual parent
public void AddChild()
{
Foo newChild = new Foo(parent:this);
this.children.Add(newChild);
}
// if you need the ability to remove items as well,
// you can expose a remove method too. Just make
// sure that you validate expected preconditions
public int RemoveChild(Foo childToRemove)
{
if(ReferenceEquals(childToRemove,null)) throw new ArgumentNullException("childToRemove");
if(!ReferenceEquals(this,childToRemove.parent)) throw new ArgumentException("The object cannot be removed because the current object is not the correct parent.","childToRemove");
return children.RemoveAll((Foo existentChild) => existentChild.Equals(childToRemove));
}
}
my version, i using extensions.
public static class EnumerableExtensions
{
/// <summary>Get max nesting level.</summary>
/// <param name="source">Source.</param>
/// <param name="children">Selector.</param>
/// <typeparam name="T">Type.</typeparam>
/// <returns><see cref="IEnumerable{T}"/>.</returns>
public static int GetMaxNestingLevel<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> children)
{
return source.SelectMany(x => GetNestingLevel(x, 0)).Max();
IEnumerable<int> GetNestingLevel(T source, int level)
{
if (children(source) != null)
{
return children(source).SelectMany(x => GetNestingLevel(x, level + 1));
}
else
{
return new List<int> { level }
}
}
}
}
than u can use it like
var max = Foo.Childs.GetMaxNestingLevel(x => x.Childs);
ps
mayby you net tests (NUnit)
using NUnit.Framework;
....
public class EnumerableExtensionsTests
{
private static IEnumerable GetMaxNestingLevelTestCases
{
get
{
yield return new TestCaseData(new int[] { 1, 2, 3, 4 }).Returns(4);
yield return new TestCaseData(new int[] { 4, 3, 2, 1 }).Returns(4);
yield return new TestCaseData(new int[] { 1, 10, 0, 1 }).Returns(10);
yield return new TestCaseData(new int[] { 1, 1, 1, 1 }).Returns(1);
yield return new TestCaseData(new int[] { 1, 1, 1, 2 }).Returns(2);
}
}
[TestCaseSource(nameof(GetMaxNestingLevelTestCases))]
public int GetMaxNestingLevelTest(ICollection<int> sourceNestingLevel)
{
var testSource = sourceNestingLevel.Select(x => new NestingLevelTestClass(x)).ToList();
return testSource.GetMaxNestingLevel(x => x.Children);
}
private class NestingLevelTestClass
{
public NestingLevelTestClass(int childrenLevel = 0)
{
if (childrenLevel != 0)
{
Children = new List<NestingLevelTestClass>
{
new NestingLevelTestClass(childrenLevel - 1),
};
}
}
public ICollection<NestingLevelTestClass> Children { get; set; }
}
}
I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);
I am using Lucene.NET and able to search get hit results as ScoreDoc[].
I need to know specific item position in ScoreDoc[]. All items in ScoreDoc[] are unique.
Sample code:
luceneSearcher.Search(query, collector);
ScoreDoc[] scores = collector.TopDocs().scoreDocs
For example, I need to get find item position in ScoreDoc[], which has custom ID property where value could be '99999'.
I can iterate through item in scores[] and check for ID property which matches '99999' then return the position, but this can have performance hit because scores[] can have thousands of items.
Is there any better technique?
Thanks
I came up with creating new ExtendedCollector which stores CollectedDocuments.
public class ExtendedCollector : Collector
{
private Scorer _scorer;
private Int32 _docBase;
private List<CollectedDocument> _documents;
public ExtendedCollector()
{
_documents = new List<CollectedDocument>();
}
public override void SetScorer(Scorer scorer)
{
_scorer = scorer;
}
public override void Collect(int doc)
{
var docId = _docBase + doc;
var score = _scorer.Score();
var currentDoc = _documents.FirstOrDefault(d => d.DocId == docId);
if (currentDoc == null)
_documents.Add(new CollectedDocument()
{DocId = docId, Score = score, OriginalIndex = _documents.Count, Index = _documents.Count});
else
currentDoc.Score = score;
}
public override void SetNextReader(IndexReader reader, int docBase)
{
_docBase = docBase;
}
public override bool AcceptsDocsOutOfOrder()
{
return false;
}
public List<CollectedDocument> Documents
{
get { return _documents; }
}
public List<CollectedDocument> DocumentsByScore
{
get
{
var result = _documents.OrderByDescending(d => d.Score).ToList();
var itemId = 0;
foreach (var collectedDocument in result)
{
itemId++;
collectedDocument.Index = itemId;
}
return result;
}
}
}
CollectedDocument looks like this
public class CollectedDocument
{
public Int32 DocId { get; set; }
public float Score { get; set; }
public int OriginalIndex { get; set; }
public int Index { get; set; }
}
Whenever you want to get results you would do
var myCollector = new ExtendedCollector();
searcher.Search(searchQuery, myCollector);
foreach (var doc in myCollector.Documents)
{
var docIndex = doc.Index; //this is the current index in a list
var originalIndex = doc.OriginalIndex; //this is item Id set when doc was collected
}
You can also get the documents ordered by score using
myCollector.DocumentsByScore
This might not be the easiest solution, but it works. If anyone has a better solution, please post it as I'd like to know that as well.