3
\$\begingroup\$

I have two IEnumerable objects called items and newItems. I need to update the Did and KeyDid property of each element in items with the matching Did and KeyDid from newItems. I can't just call items = newItems; since other things have a reference to items. Here's my code:

T[] arrItems = items as T[] ?? items.ToArray();
if (arrItems.Length != newItems.Count())
    throw new Exception("Item counts do not match.");

//Copy Did and KeyDid from old items to newItems
int i = 0;
foreach (T newItem in newItems)
{
    T oldItem = arrItems[i++];
    oldItem.Did = newItem.Did;
    oldItem.KeyDid = newItem.KeyDid;
}

I'm interested in a more concise way to do this, perhaps using a single LINQ expression. Any ideas how to improve this code in terms of readability, clearness, conciseness, or overall coolness?

\$\endgroup\$
  • \$\begingroup\$ Not sure about the only enumerate once comment. You are possible already enumerating twice by doing the .Count() call earlier??? \$\endgroup\$ – dreza May 8 '14 at 0:58
  • \$\begingroup\$ @dreza - Good call. The .Count() call should be moved to after the .ToArray() - I'll update the code. \$\endgroup\$ – Mike Christensen May 8 '14 at 1:57
2
\$\begingroup\$
 if (arrItems.Length != newItems.Count())
        // --> BAD throw new Exception("Item counts do not match.");
        // Do not throw the Exception base here look for the real exception for example ArgumentException(if the arr come from arguments) or your custom exception:
        throw new ArgumentException();

      //Copy Did and KeyDid from old items to newItems
      for (var index = 0; index < newItems.Length; index++)
      {
        arrItems[index].Did = newItems[index].Did;
        arrItems[index].KeyDid = newItems[index].KeyDid;
      }

I do not think LINQ will give you a better solution than what you have above; you can use group by or select to create a new element in the linq query and than put it back to object! "For" in this case is very fast and it solve the problem.

\$\endgroup\$
  • \$\begingroup\$ Thanks! Yea looks like just looping through the list is the way to go. The Exception was just for demonstration purposes; we have our own exception classes in our actual code. \$\endgroup\$ – Mike Christensen May 8 '14 at 17:12
1
\$\begingroup\$

How about this -

if (arrItems.Length == newItems.Count())
{
    newItems = newItems.Zip(arrItems, (newItem, arrItem) =>
        {
            newItem.KeyDid = arrItem.KeyDid;
            newItem.Did = arrItem.Did;
            return newItem;
        });                
}

The Zip method basically merges two enumerables by invoking the specified delegate and passing the items of the two arrays as parameters. Makes for concise code.

\$\endgroup\$
  • \$\begingroup\$ Looks like you're copying from arrItems to newItems which is the opposite of what I want. Either way, this approach won't work. You're creating a new list, which is then lost when the function exits. Plus, the code would never be run unless you materialized the list. This approach seems hacky to me. \$\endgroup\$ – Mike Christensen May 8 '14 at 17:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.