#c# #numbers
#c# #числа
Вопрос:
У меня есть часть в моем приложении, которая должна что-то делать (=> добавить дополнение 0 перед другими числами), когда указанное число получает дополнительную цифру, что означает, что оно получает 10, 100, 1000 или так далее…
На данный момент я использую для этого следующую логику:
public static bool IsNewDigit(this int number)
{
var numberString = number.ToString();
return numberString.StartsWith("1")
amp;amp; numberString.Substring(1).All(c => c == '0');
}
Я могу сделать:
if (number.IsNewDigit()) { /* add padding 0 to other numbers */ }
Мне это кажется «взломом» с использованием преобразования строк.
Есть ли что-то лучшее (может быть, даже «встроенное») для этого?
Обновить:
Один из примеров, когда мне это нужно:
у меня есть элемент со следующей (упрощенной) структурой:
public class Item
{
public int Id { get; set; }
public int ParentId { get; set; }
public int Position { get; set; }
public string HierarchicPosition { get; set; }
}
HierarchicPosition
— это собственная позиция (с дополнением) и родительские HierarchicPositon
элементы. Например. элемент, который является 3-м дочерним элементом из 12 из элемента в позиции 2, имеет 2.03
в качестве своего HierarchicPosition
. Это также может быть что-то более сложное, например 011.7.003.2.02
.
Затем это значение используется для очень простой сортировки элементов в «древовидной» структуре.
Теперь у меня есть IQueryable<Item>
и хочу добавить один элемент в качестве последнего дочернего элемента другого элемента. Чтобы избежать необходимости воссоздавать все HierarchicalPosition
, что я хотел бы обнаружить (с учетом рассматриваемой логики), если новая позиция добавляет новую цифру:
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);
ОБНОВЛЕНИЕ # 2:
Я запрашиваю эту строку позиции из базы данных, например:
var items = db.Items.Where(...)
.OrderBy(i => i.HierarchicPosition)
.Skip(pageSize * pageNumber).Take(pageSize);
Из-за этого я не могу использовать an IComperator
(или что-то еще, что сортируется «через код»).
Это вернет элементы с 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
ОБНОВЛЕНИЕ # 3:
Мне нравится альтернативное решение со double
значениями, но у меня есть несколько «более сложных случаев», таких как следующие, я не уверен, что смогу решить это:
я создаю (на части многих) галерею изображений, в которой есть Categories
и Images
. Там у категории может быть родитель и несколько дочерних элементов, и каждое изображение принадлежит категории (я назвал их Holder
и Asstes
в моей логике — поэтому у каждого изображения есть держатель, и каждая категория может иметь несколько активов). Эти изображения сортируются сначала по позиции категорий, а затем по собственной позиции. Это я делаю, комбинируя HierarchicPosition
подобное HolderHierarchicPosition#ItemHierarchicPosition
. Таким образом, в категории, которая имеет 02.04
в качестве своей позиции и 120 изображений, будет получено 3-е изображение 02.04#003
.
У меня есть даже несколько случаев с «тремя уровнями» (или, может быть, больше в будущем), например 03.1#02#04
.
Могу ли я адаптировать «двойное решение» для поддержки таких сценариев?
PS: Я также открыт для другого решения моей базовой проблемы.
Комментарии:
1. Я не понял вашего вопроса. можете ли вы предоставить некоторые примеры входных данных и желаемых результатов? 🙂
2. У вас есть доступ к «предыдущему» номеру?
3. Как насчет использования
Math.Log10
метода ? Вы можете проверить, равен результатint
или нет, например.. Это не удается только1
для вашего примера.4.
return number.ToString().Length > (number - 1).ToString().Length;
5. Если вы выполняете только заполнение, почему бы вам не использовать одну из функций форматирования чисел
ToString("D4")
?;
Ответ №1:
Вы могли бы проверить, является ли логарифм числа по основанию 10 целым числом. (10 -> 1, 100 -> 2, 1000 -> 3, …)
Это также может немного упростить ваш алгоритм в целом. Вместо того, чтобы добавлять один 0 заполнения каждый раз, когда вы находите что-то большее, просто отслеживайте максимальное число, которое вы видите, затем возьмите length = floor(log10(number)) 1
и убедитесь, что все дополнено length
. Эта часть не страдает от арифметических проблем с плавающей запятой, таких как сравнение с целым числом.
Комментарии:
1. довольно хрупкий. Выполнение
F(x) == 1.0
ненадежно.2. Верно, но оно верно / согласовано в пределах известного диапазона — просто проверьте его до MAX_INT раньше — оно соответствует.
Ответ №2:
Из того, что вы описываете, похоже, что ваша HierarchicPosition
позиция должна поддерживать порядок элементов, и вы сталкиваетесь с проблемой, что, когда у вас есть идентификаторы 1..9
и добавьте a 10
, вы 1,10,2,3,4,5,6...
где-то получите порядок и, следовательно, захотите 01,02,03...,10
исправить-слева направо?
Если я прав, пожалуйста, сначала взгляните на это: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
Потому что то, что вы пытаетесь сделать, является обходным путем для решения проблемы определенным образом. — Но могут быть более эффективные способы действительно решить эту проблему. (поэтому вам следовало бы лучше спросить о вашей реальной проблеме, а не о решении, которое вы пытаетесь реализовать)
Смотрите Здесь решение, используя пользовательский IComparator для сортировки строк (которые на самом деле являются числами) обычным способом: http://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C
Обновление, касающееся вашего обновления:
Предоставляя сортирующую «строку», как вы это делаете, вы можете вставить элемент «где-нибудь» без переиндексации ВСЕХ последующих элементов, как это было бы для целочисленного значения. (Похоже, это и есть цель)
Вместо создания сложной «строки» вы могли бы использовать двойное значение для быстрого достижения того же результата:
Если вы вставляете элемент где-то между 2 существующими элементами, все, что вам нужно сделать, это: this.sortingValue = (prior.sortingValue next.sortingValue) / 2
и обрабатывать регистр при вставке в конец списка.
Предположим, вы добавляете элементы в следующем порядке:
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
Когда вы теперь выполняете простую сортировку в зависимости от этого двойного поля, порядок будет:
100.00 -> 1
150.00 -> 1.1
175.00 -> 1.2
200.00 -> 2
Хотите добавить 1.1.1
? Отлично: positon = (150.00 175.00)/2;
;
вы могли бы просто умножить все на 10, всякий раз, когда ваше НОВОЕ значение достигает x.5
*, чтобы убедиться, что у вас не заканчиваются десятичные разряды (но вам не нужно — наличие .5 .25 .125 ...
не повредит сортировке):
Итак, после добавления 1.1.1
значения, которое будет 162,5
, умножьте все на 10:
1000.00 -> 1
1500.00 -> 1.1
1625.00 -> 1.1.1
1750.00 -> 1.2
2000.00 -> 2
Итак, всякий раз, когда вы перемещаете элемент вокруг, вам нужно только пересчитать положение n
, посмотрев на n-1
и n 1
В зависимости от ожидаемого количества дочерних элементов для каждой записи, вы можете начать с «1000.00», «10.000» или любого другого, который подходит лучше всего.
Чего я не учел: когда вы хотите переместить «2» наверх, вам нужно будет пересчитать все дочерние элементы «2», чтобы получить значение где-то между значением сортировки «2» и теперь «следующий» элемент… Может вызвать некоторую головную боль 🙂
Решение с «двойными» значениями имеет некоторые ограничения, но будет работать для небольших наборов групп. Однако вы говорите о «Группах, подгруппах и изображениях с количеством 100», поэтому предпочтительнее другое решение:
- Во-первых, вам следует реорганизовать свою базу данных: в настоящее время вы пытаетесь «сжать» дерево в список (таблицы данных — это в основном списки)
- Чтобы действительно отразить сложную компоновку дерева с бесконечной глубиной, вы должны использовать 2 таблицы и реализовать
composite pattern
. - Затем вы можете использовать рекурсивный подход, чтобы получить категорию, ее подкатегорию, […] и, наконец, элементы этой категории.
- При этом вам нужно только указать положение каждого листа в его текущем узле.
- Перестановка листьев не повлияет ни на один лист другого узла или любого узла.
- Перераспределение узлов не повлияет ни на один подузел или лист этого узла.
Комментарии:
1.
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?
Да, это основная проблема. Я посмотрю на предложенные вами ссылки…2. Я не могу использовать пользовательский
IComperator
, поскольку я выполняю эту сортировку (и подкачку) «в БД» — я добавлю это к вопросу.3. @chrfin почему бы не изменить «HierarchicPosition» на целочисленное значение? Они могут быть отсортированы без каких-либо усилий.
4. Потому что у меня не может быть «нескольких уровней» (см. Пример в вопросе) в «одном целом числе».
5. ЭТО хорошо — мне нужно проверить это с помощью моего «самого сложного варианта использования» (см. Обновление # 3 к вопросу)…
Ответ №3:
Вы можете проверить сумму квадратов всех цифр для ввода, 10,100,1000 имеет нечто общее, что, если вы вычисляете сумму квадратов всех цифр, она должна сходиться к единице;
10
1^2 0^2 = 1
100
1^2 0^2 0^2 = 1
и так далее и тому подобное.