Reputation: 23103
I have a part in my application which needs to do do something (=> add padding 0 in front of other numbers) when a specified number gets an additional digit, meaning it gets 10, 100, 1000 or so on...
At the moment I use the following logic for that:
public static bool IsNewDigit(this int number)
{
var numberString = number.ToString();
return numberString.StartsWith("1")
&& numberString.Substring(1).All(c => c == '0');
}
The I can do:
if (number.IsNewDigit()) { /* add padding 0 to other numbers */ }
This seems like a "hack" to me using the string conversion.
Is there something something better (maybe even "built-in") to do this?
UPDATE:
One example where I need this:
I have an item with the following (simplified) structure:
public class Item
{
public int Id { get; set; }
public int ParentId { get; set; }
public int Position { get; set; }
public string HierarchicPosition { get; set; }
}
HierarchicPosition
is the own position (with the padding) and the parents HierarchicPositon
. E.g. an item, which is the 3rd child of 12 from an item at position 2 has 2.03
as its HierarchicPosition
. This can as well be something more complicated like 011.7.003.2.02
.
This value is then used for sorting the items very easily in a "tree-view" like structure.
Now I have an IQueryable<Item>
and want to add one item as the last child of another item. To avoid needing to recreate all HierarchicalPosition
I would like to detect (with the logic in question) if the new position adds a new digit:
Item newItem = GetNewItem();
IQueryable<Item> items = db.Items;
var maxPosition = items.Where(i => i.ParentId == newItem.ParentId)
.Max(i => i.Position);
newItem.Position = maxPosition + 1;
if (newItem.Position.IsNewDigit())
UpdateAllPositions(items.Where(i => i.ParentId == newItem.ParentId));
else
newItem.HierarchicPosition = GetHierarchicPosition(newItem);
UPDATE #2:
I query this position string from the DB like:
var items = db.Items.Where(...)
.OrderBy(i => i.HierarchicPosition)
.Skip(pageSize * pageNumber).Take(pageSize);
Because of this I can not use an IComperator
(or something else wich sorts "via code").
This will return items with HierarchicPosition
like (pageSize = 10
):
03.04
03.05
04
04.01
04.01.01
04.01.02
04.02
04.02.01
04.03
05
UPDATE #3:
I like the alternative solution with the double
values, but I have some "more complicated cases" like the following I am not shure I can solve with that:
I am building (on part of many) an image gallery, which has Categories
and Images
. There a category can have a parent and multiple children and each image belongs to a category (I called them Holder
and Asstes
in my logic - so each image has a holder and each category can have multiple assets). These images are sorted first be the categories position and then by its own position. This I do by combining the HierarchicPosition
like HolderHierarchicPosition#ItemHierarchicPosition
. So in a category which has 02.04
as its position and 120 images the 3rd image would get 02.04#003
.
I have even some cases with "three levels" (or maybe more in the future) like 03.1#02#04
.
Can I adapt the "double solution" to suport such scenarios?
P.S.: I am also open to other solution for my base problem.
Upvotes: 1
Views: 3794
Reputation: 21
You could check sum of square of all digits for the input, 10,100,1000 has something in common that, if you do the sum of square of all digits, it should converge to one;
10 1^2 + 0^2 = 1 100 1^2 + 0^2 + 0^2 = 1
so on so forth.
Upvotes: 1
Reputation: 20899
From What you describe, it looks like your HierarchicPosition
position should maintain an order of items and you run into the problem, that when you have the ids 1..9
and add a 10
, you'll get the order 1,10,2,3,4,5,6...
somewhere and therefore want to pad-left to 01,02,03...,10
- correct?
If I'm right, please have a look at this first: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
Because what you try to do is a workarround to solve the problem in a certain way. - But there might be more efficent ways to actually really solve it. (therefore you should have better asked about your actual problem rather than the solution you try to implement)
See here for a solution, using a custom IComparator to sort strings (that are actually numbers) in a native way: http://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C
Update regarding your update:
With providing a sorting "String" like you do, you could insert a element "somewhere" without having ALL subsequent items reindexed, as it would be for a integer value. (This seems to be the purpose)
Instead of building up a complex "String", you could use a Double-Value to achieve the very same result real quick:
If you insert an item somewhere between 2 existing items, all you have to do is : this.sortingValue = (prior.sortingValue + next.sortingValue) / 2
and handle the case when you are inserting at the end of the list.
Let's assume you add Elements in the following order:
1 First Element // pick a double value for sorting - 100.00 for example. -> 100.00
2 Next Element // this is the list end - lets just add another 100.00 -> 200.00
1.1 Child // this should go "in between": (100+200)/2 = 150.00
1.2 Another // between 1.1 and 2 : (150+200)/2 = 175
When you now simple sort depending on that double field, the order would be:
100.00 -> 1
150.00 -> 1.1
175.00 -> 1.2
200.00 -> 2
Wanna Add 1.1.1
? Great: positon = (150.00 + 175.00)/2;
;
you could simple multiply all by 10, whenever your NEW value hits x.5
* to ensure you are not running out of decimal places (but you dont have to - having .5 .25 .125 ...
does not hurt the sorting):
So, after adding the 1.1.1
which would be 162,5
, multiply all by 10:
1000.00 -> 1
1500.00 -> 1.1
1625.00 -> 1.1.1
1750.00 -> 1.2
2000.00 -> 2
So, whenever you move an item arround, you only need to recalculate the position of n
by looking at n-1
and n+1
Depending on the expected childs per entry, you could start with "1000.00", "10.000" or whatever matches best.
What I didn't take into account: When you want to move "2" to the top, you would need to recalculate all childs of "2" to have a value somewhere between the sorting value of "2" and the now "next" item... Could serve some headache :)
The solution with "double" values has some limitations, but will work for smaller sets of groups. However you are talking about "Groups, subgroups, and pictures with counts of 100" - so another solution would be preferable:
composite pattern
. Upvotes: 1
Reputation: 34155
You could check if base-10 logarithm of the number is an integer. (10 -> 1, 100 -> 2, 1000 -> 3, ...)
This could also simplify your algorithm a bit in general. Instead of adding one 0 of padding every time you find something bigger, simply keep track of the maximum number you see, then take length = floor(log10(number))+1
and make sure everything is padded to length
. This part does not suffer from the floating point arithmetic issues like the comparison to integer does.
Upvotes: 1