Вы могли использовать несколько запросов, которые используют Take
и Skip
, но это добавит слишком много итераций на Исходный список, я считаю.
Скорее, я думаю, что вы должны создать собственный итератор, например:
public static IEnumerable> GetEnumerableOfEnumerables(
IEnumerable enumerable, int groupSize)
{
// The list to return.
List list = new List(groupSize);
// Cycle through all of the items.
foreach (T item in enumerable)
{
// Add the item.
list.Add(item);
// If the list has the number of elements, return that.
if (list.Count == groupSize)
{
// Return the list.
yield return list;
// Set the list to a new list.
list = new List(groupSize);
}
}
// Return the remainder if there is any,
if (list.Count != 0)
{
// Return the list.
yield return list;
}
}
Затем вы можете вызвать это, и LINQ включен так, вы можете выполнять другие операции над результирующими последовательностями.
В ответ на ответ Сэма , я чувствовал, что существует более простой способ сделать это без:
Итак, вот еще один проход, который я кодировал в методе расширения до IEnumerable
Chunk
:
public static IEnumerable> Chunk(this IEnumerable source,
int chunkSize)
{
// Validate parameters.
if (source == null) throw new ArgumentNullException("source");
if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize",
"The chunkSize parameter must be a positive value.");
// Call the internal implementation.
return source.ChunkInternal(chunkSize);
}
Ничего удивительного там, просто базовая проверка ошибок.
Переход к ChunkInternal
:
private static IEnumerable> ChunkInternal(
this IEnumerable source, int chunkSize)
{
// Validate parameters.
Debug.Assert(source != null);
Debug.Assert(chunkSize > 0);
// Get the enumerator. Dispose of when done.
using (IEnumerator enumerator = source.GetEnumerator())
do
{
// Move to the next element. If there's nothing left
// then get out.
if (!enumerator.MoveNext()) yield break;
// Return the chunked sequence.
yield return ChunkSequence(enumerator, chunkSize);
} while (true);
}
В принципе , он получает IEnumerator
и вручную выполняет итерацию через каждый элемент. Он проверяет, есть ли какие-либо элементы, которые в настоящее время перечислены. После того, как каждый фрагмент перечислит, если не осталось никаких элементов, он выйдет из строя.
Как только он обнаруживает, что есть элементы в последовательности, он делегирует ответственность за внутреннюю реализацию IEnumerable
до ChunkSequence
:
private static IEnumerable ChunkSequence(IEnumerator enumerator,
int chunkSize)
{
// Validate parameters.
Debug.Assert(enumerator != null);
Debug.Assert(chunkSize > 0);
// The count.
int count = 0;
// There is at least one item. Yield and then continue.
do
{
// Yield the item.
yield return enumerator.Current;
} while (++count < chunkSize && enumerator.MoveNext());
}
Поскольку MoveNext
уже вызывается на IEnumerator
, переданном в ChunkSequence
, он возвращает элемент, возвращенный Current
, а затем увеличивает счетчик, чтобы никогда не возвращать больше, чем chunkSize
элементов и переходить к следующему элементу в последовательности после каждой итерации (но закорочен, если количество полученных элементов превышает размер куска).
Если никаких элементов не осталось, то метод InternalChunk
сделает другой проход во внешнем цикле, но когда MoveNext
вызывается второй раз, он все равно вернет false, в качестве в документации (выделенное мое):
Если MoveNext передает конец коллекции, перечислитель помещается после последнего элемента в коллекции, а MoveNext возвращает false. Когда перечислитель находится в этой позиции, последующие вызовы MoveNext также возвращают false до тех пор, пока не вызывается Reset.
В этот момент цикл прерывается, и последовательность последовательностей прекращается.
Это простой тест:
static void Main() { string s = "agewpsqfxyimc"; int count = 0; // Group by three. foreach (IEnumerable
g in s.Chunk(3)) { // Print out the group. Console.Write("Group: {0} - ", ++count); // Print the items. foreach (char c in g) { // Print the item. Console.Write(c + ", "); } // Finish the line. Console.WriteLine(); } } Выход:
Group: 1 - a, g, e, Group: 2 - w, p, s, Group: 3 - q, f, x, Group: 4 - y, i, m, Group: 5 - c,
Важно отметить, что не работает, если вы не сливайте всю последовательность потомков или не разбивайте их в любой точке родительской последовательности. Это важный аспект, но если ваш случай использования заключается в том, что вы будете потреблять каждый элемент последовательности последовательностей, то это будет работать для вас.
Кроме того, он будет делать странные вещи, если вы играете с порядком, так же, как Сэм делал в какой-то момент .
Я думаю, вы можете найти свой ответ здесь:
Слушатель для выбора текста Android
Ключевой термин, который вы ищете здесь,
ActionMode
, если ваша цель сотовая или более новая.API docs (прокрутите вниз до «с использованием режима контекстного действия») хорошая работа по разъяснению вещей, как только вы найдете то, что ищете, что является самым большим препятствием для их использования, но в основном то, что вам нужно сделать, это следующее:
- установите
EditText
для выбора (android:textIsSelectable="true"
илиsetTextIsSelectable(true);
- Внедрите интерфейс
ActionMode.Callback
и укажите свои собственные пункты меню.ПРИМЕЧАНИЕ: как упомянутый выше, это работает только для уровня API 11+. Если вы ориентируетесь на более ранние платформы, получение событий для выбора текста намного сложнее.
blockquote>
в .xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true" />
в .class:
textview.setCustomSelectionActionModeCallback(new callback(textview));
...
public class callback implements Callback {
private TextView mTextView;
public callback(TextView text) {
this.mTextView = text;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
int start = mTextView.getSelectionStart();
int end = mTextView.getSelectionEnd();
Spannable wordtoSpan = (Spannable) mTextView.getText();
switch (item.getItemId()) {
case R.id.item_blue:
wordtoSpan.setSpan(new BackgroundColorSpan(Color.BLUE), start
, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
singlenton.getInstance().getDatabase().createMarkText(mTextView,Color.BLUE);
return true;
case R.id.item_green:
wordtoSpan.setSpan(new BackgroundColorSpan(Color.GREEN), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
singlenton.getInstance().getDatabase().createMarkText(mTextView,Color.GREEN);
return true;
case R.id.item_red:
wordtoSpan.setSpan(new BackgroundColorSpan(Color.RED), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
singlenton.getInstance().getDatabase().createMarkText(mTextView,Color.RED);
return true;
case R.id.item_yellow:
wordtoSpan.setSpan(new BackgroundColorSpan(Color.YELLOW), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
singlenton.getInstance().getDatabase().createMarkText(mTextView,Color.YELLOW);
return true;
case R.id.item_erase:
wordtoSpan.setSpan(new BackgroundColorSpan(Color.TRANSPARENT), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
singlenton.getInstance().getDatabase().createMarkText(mTextView,Color.TRANSPARENT);
return true;
}
return false;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTitle("Selecione a cor");
mode.getMenuInflater().inflate(R.menu.menu_text_context, menu);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
menu.removeItem(android.R.id.selectAll);
// Remove the "cut" option
menu.removeItem(android.R.id.cut);
// Remove the "copy all" option
menu.removeItem(android.R.id.copy);
return true;
}
}