2015-10-08 2 views
0

У меня есть простой запрос LINQ, который я хотел бы закоротить на основе первого совпадающего значения в массиве. Я просто хочу вернуться на основе первого значения в xmlDoc, который соответствует первому элементу массива, например, если он соответствует «B», то он выйдет, если он этого не сделает, он попытается сопоставить «C» и т. д.LINQ + короткое замыкание на основе массива

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

Большое спасибо,

йог

string[] searchTypes = { "B", "C", "D", "A" }; 
XDocument xDoc = XDocument.Parse(xmlString); 

for (int i = 0; i < searchTypes.Length; i++) 
    commsValue = (from r in xDoc.Descendants("Root") 
        where r.Element("dCode") != null && r.Element("dCode").Value == searchTypes[i]) 
select r.Element("Number").Value).FirstOrDefault(); 
if (commsValue == null) 
       { 
        continue; 
       } 

}

+5

Было бы намного проще, чтобы помочь вам, если вы хотите обеспечить короткий, но полный * * Например, с вводом образца и ожидаемого результата. Я не следую тому, что вы пытаетесь сделать в данный момент. –

+1

Если «элегантным» вы подразумеваете запрос Linq, который очевидно для тех, кто читает его, что он делает, тогда нет, нет «элегантного» решения. Придерживайтесь петлей, которую вы можете понять и уметь объяснить. –

+0

Что-то вроде 'searchTypes.Contains (r.Element (" dCode "). Value.ToString())'? –

ответ

2

Нет, но вы можете сделать свой цикл один цикл xDoc.Descendants("Root") (который LINQ петли внутри) и сократить поиск массива ,

Сначала создайте словарь подстановок из searchTypes:

var lookup = new [] { "B", "C", "D", "A" } 
    .Select((e, i) => new {Key = e, Index = i}) 
    .ToDictionary(i => i.Key, i => i.Index); 

Теперь у вас есть "B"0, для "C" у вас есть 1 и так далее.

Теперь для фактического цикла:

int curIndex = int.MaxValue; 
foreach(var r in xDoc.Descendants("Root")) 
{ 
    int index; 
    if (r.Element("dCode") != null && lookup.TryGetValue(r.Element("dCode").Value, out index)) 
    { 
    if (index == 0) 
    { 
     commsValue = r.Element("Number").Value; 
     break; // short-circuit 
    } 
    else if (index < curIndex) 
    { 
     commsValue = r.Element("Number").Value; // Value to have after finished loop. 
     curIndex = index; 
    } 
    } 
} 
0

Там нет встроенного в способ сделать короткое замыкание, что я в курсе. Как вы сформулировали свой вопрос, он будет искать весь XML-узел для «B», а если он не будет найден, то выполните поиск всего нодлиста для «C» и т. Д.

Трудно получить хорошее ответьте за вас без более подробной информации. Как часто ожидается «B», и сколько узлов имеет XML-документ? Вы можете пройти весь нодлист, отслеживая наилучшее совпадение, когда вы идете, выходите, когда вы находите первый «B», или в конце, а затем возвращаете лучший результат, отличный от B. Но такой встроенный метод LINQ не существует, но вы можете создать свой собственный пользовательский интерфейс.

public static XmlNode FindBestNode(this IEnumerable<XmlNode> nodes, string[] vals) 
{ 
    XmlNode best=null; 
    int bestindex=vals.length+1; 
    while(nodes.MoveNext()) 
    { 
    var currindex=vals.indexOf(nodes.Current.Element("dCode").Value)); 
    if (currindex>=0) { 
     if (currindex==0) return nodes.Current; 
     if (currindex<bestindex) { 
     bestindex=currindex; 
     best=nodes.Current; 
    } 
    } 
    return best; 
} 

Я не эксперт в xml-запросах, но приведенное выше должно быть довольно близко.

Используется как:

string[] searchTypes = { "B", "C", "D", "A" }; 
XDocument xDoc = XDocument.Parse(xmlString); 
var bestNode=xDoc.Descendants("Root") 
    .Where(r=>r.Element("dCode")!=null) 
    .FindBestNode(searchTypes); 
+0

ОК, я обновил код, чтобы показать, как я буду делать это в forLoop.Спасибо – yoker

+0

Это почти тот же ответ, что и у Джона внизу, но возвращает фактический узел вместо значения, он не использует словарь и завернут в метод расширения. На самом деле он лучше, поэтому я отвечу его ответом. Я буду держать это здесь только для примера, но вы должны использовать его ответ. Я считаю, что его использование словаря в большинстве случаев будет масштабироваться лучше, чем 'Array.indexOf' –

0

Вы можете использовать тот факт, что в объединении порядка сортировки первой коллекции сохраняются. Так присоединиться к serachTypes с xDoc.Descendants("Root") и взять первый элемент:

string[] searchTypes = { "B", "C", "D", "A" }; 
XDocument xDoc = XDocument.Parse(xmlString); 

var commsValue = (from st in searchTypes 
        join r in xDoc.Descendants("Root") 
         on st equals r.Element("dCode").Value 
        where r.Element("dCode") != null) 
       .FirstOrDefault();