2017-02-06 1 views
1

Есть ли способ в Linq установить значение для первого выбранного вхождения, соответствующее условию?Linq Выбрать: Установить только определенное значение для первого вхождения

Я знаю, что я не объяснить это хорошо, но, к примеру:

var mySelectedObjects = allMyObjects.OrderBy(o.Date).Select(o => new ObjectType 
{ 
    Element1 = o.Element1, 
    Element2 = o.Element2, 
    Date = o.Date, 
    BooleanElement = o.BooleanElement && ThisIsTheFirstTrue 
}; 

Таким образом, в приведенном выше примере, желаемый результат вывода является то, что в mySelectedObjects пункт будет единственно верным для первой происхождение allMyObjects (упорядочено по Date), где это правда, а все остальные будут ложными независимо от их значений в allMyObjects.

Т.е. я хочу только одно (самое раннее) возникновение с = true в mySelectedObjects. Что я должен заменить ThisIsTheFirstTrue?

Редактировать

Я понимаю, что это может быть сделано с помощью for петли после того выбора, но я надеюсь, что все это сделать в Select.

В любом случае, просто уточнить в ответ на предложения. Я хочу, чтобы это было правдой для первого вхождения, где это правда на входе, а не только первое вхождение, например, если входные данные ...

false 
true 
false 
true 
true 
false 

Я хочу выход быть

false 
true 
false 
false 
false 
false 
+0

Для правки - почему другие значения два 'true' пошли на выходе? –

+0

@ Сергий Березовский - это требование того, что я пытаюсь сделать. (В этом случае логическое значение привязано к атрибуту «Видимость» в форме XAML, и я хочу отображать только один элемент каждый раз) – colmde

+0

Должна ли эта функциональность быть реализована в стандарте Select или является общим основанием Linq? – TheJP

ответ

3

Таким образом, используя Linq:

bool found = false; 
var mySelectedObjects = objects 
    .OrderBy(o => o.Date) 
    .Select(o => new ObjectType 
    { 
     Element1 = o.Element1, 
     Element2 = o.Element2, 
     Date = o.Date, 
     BooleanElement = !found && o.BooleanElement ? (found = true) : false 
    } 
); 

Online check

+0

О, это умная вставка по сравнению с моим ответом :) –

+0

Я начал писать это, когда был один ответ. но в настоящее время я нахожусь на рабочем месте, поэтому у меня есть что-то делать, кроме как просто отвечать :) –

+1

Мне не нравится изменять локальные переменные в запросе LINQ как побочный эффект. Вы можете получить неожиданные результаты, если кто-то слегка меняет запрос –

2

я бы назначить его в петле, не элегантно, но работает:

var mySelectedObjects = allMyObjects 
    .OrderBy(o.Date) 
    .Select(o => new ObjectType 
    { 
     Element1 = o.Element1, 
     Element2 = o.Element2, 
     Date = o.Date, 
     BooleanElement = o.BooleanElement 
    }); 

bool firstTrueFound = false; 
foreach(ObjectType o in mySelectedObjects) 
{ 
    if(o.BooleanElement) 
    { 
     if(firstTrueFound) 
      o.BooleanElement = false; 
     else 
      firstTrueFound = true; 
    } 
} 

Единственное решение LINQ, что приходит мне в голову не так эффективно, смотрите:

var boolLookup = allMyObjects.ToLookup(o => o.BooleanElement); 
var mySelectedObjects = boolLookup[true] 
    .OrderBy(o => o.Date) 
    .Select((o, index) => new ObjectType 
    { 
     Element1 = o.Element1, 
     Element2 = o.Element2, 
     Date = o.Date, 
     BooleanElement = index == 0 
    }) 
    .Concat(boolLookup[false]) 
    .OrderBy(o => o.Date); 
+0

как насчет запроса в linq? – Mafii

+0

@Mafii: что за 'let'? В синтаксисе запроса вы даже не получаете индекс в select (в отличие от синтаксиса метода, где вы можете использовать перегрузку select, которая дает индекс). Но этот индекс не помогает, потому что вам нужен индекс первого появления 'BooleanElement' в упорядоченном запросе. –

+0

Ты прав, не думал об этом. – Mafii

1

То, что вы просите, то есть, проверка индекса элемента внутри Select не представляется возможным (на самом деле, при работе над списком и вызова IndexOf, но это квадратичная с точки зрения эксплуатационных расходов, так что я не предполагал бы этого). Однако обратите внимание, что внутри select вам необязательно писать выражение для создания объекта, вы также можете написать блок кода. Таким образом, следующие будут работать:

bool found = false; 
var mySelectedObjects = allMyObjects.OrderBy(o.Date).Select(o => 
{ 
    var booleanElement = !found && o.BooleanElement; 
    found = booleanElement; 

    var result = new ObjectType 
    { 
     Element1 = o.Element1, 
     Element2 = o.Element2, 
     Date = o.Date, 
     BooleanElement = booleanElement 
    }; 

    return result; 
}); 

Это самый LINQish способ ее решения, что я могу предоставить вам.

Для решения замечания @TheJP, вот путь по вопросу о параллельности:

Давайте создадим класс вроде этого:

class TrueOnlyOnFirstAccess 
{ 
    private bool val = true; 
    private object valLock = new object(); 
    public bool Value 
    { 
     get 
     { 
      if (!val) return false; 

      lock (valLock) 
      { 
       if (!val) return false; 

       val = false; 
       return true; 
      } 
     } 
    } 
} 

Что это делает, когда значение сначала запрос, он будет возвращать true в первый раз и в контексте блокировки изменяет значение на false. Поскольку, как только значение является ложным, оно никогда не должно меняться, мы можем безопасно вернуть значение false, если мы обнаружим, что поле поддержки ложно, поэтому избегаем ненужных блокировок.Как использовать:

TrueOnlyOnFirstAccess switcher = new TrueOnlyOnFirstAccess(); 
items.Select(item => new 
{ 
    // other props 
    BooleanElement = item.BooleanElement && switcher .Value 
}) 

Здесь мы используем ленивую природу логического and - то есть, item.BooleanElementдолжен быть первым, чтобы оценить, если это ложь, sw.Value не будет называться, а поле подложки поэтому не будут изменены. Используйте его с осторожностью в более сложных выражениях.

+0

Проблема с этим подходом заключается в том, что он не является чистым/имеет побочные эффекты. Это может вызвать проблемы, если вы хотите запустить его параллельно. В противном случае это лучшее решение. – TheJP

+0

Абсолютно, хотя никогда не было сказано, что параллелизм является требованием. –

+2

Ну, это не LINQ. Часть выбора должна быть в foreach –