4

How do I find and replace a property using Linq in this specific scenario below:

public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
    public Property[] Properties { get; set; }

    public Property this[string name]
    {
        get { return Properties.Where((e) => e.Name == name).Single(); }
        //TODO: Just copying values... Find out how to find the index and replace the value 
        set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
    }
}

Thanks for helping out in advance.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Vyas Bharghava
  • 6,372
  • 9
  • 39
  • 59
  • What PropertyBag are you using? I don't think there's one in the .Net BCL. I ask because the 'Properties' getter is probably the one doing the cloning, hence the reason why you can't do what you're trying to. – Jonathan Rupp Apr 14 '09 at 23:49
  • Jonathan, this my own custom PropertyBag class.. The code above works because I'm not replacing the entire item in the Property[] array. The comment in the code illustrates that: //TODO: Just copying values... Find out how to find the index and replace the value – Vyas Bharghava Apr 14 '09 at 23:55
  • Just wondered why you did not use my LINQ-free IndexOf() solution ... now I know! I corrected my answer from instance to static method. – Daniel Brückner Apr 16 '09 at 11:54

2 Answers2

6

Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.

// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);

if (index != -1)
{
   this.Properties[index] = value;
}
else
{
   throw new ArgumentOutOfRangeException();
}

Why are Array.Sort() and Array.IndexOf() methods static?

Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.

this.Properties[name] = value;

Note that neither solution is thread safe.


An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.

this.Properties = Enumerable.Union(
   this.Properties.Where(p => p.Name != name),
   Enumerable.Repeat(value, 1)).
   ToArray();
Community
  • 1
  • 1
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • 6
    "Do not use LINQ because it will not improve the code" without justification, this statement is useless – spender Apr 15 '09 at 00:04
  • @danbruc: Unfortunately, using [I]Dicitonary is not option as it's not serializable... Have to leave it an array... yeah... IndexOf() seems a reasonable solution... but I was just hoping may be I was doing something wrong and there's indeed a way in Linq to do this... – Vyas Bharghava Apr 15 '09 at 00:39
0

[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(] Is your 'Property' a class or a struct?

This test passes for me:

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
    public Property[] Properties { get; set; }

    public Property this[string name]
    {
        get { return Properties.Where((e) => e.Name == name).Single(); }
        set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
    }
}

[TestMethod]
public void TestMethod1()
{
    var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
    Assert.AreEqual("Y", pb["X"].Value);
    pb["X"] = new Property { Name = "X", Value = "Z" };
    Assert.AreEqual("Z", pb["X"].Value);
}

I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.

Jonathan Rupp
  • 15,522
  • 5
  • 45
  • 61
  • It's a class... Comment re-pasted from above: The code above works because I'm not replacing the entire item in the Property[] array. The comment in the code illustrates that: //TODO: Just copying values... Find out how to find the index and replace the value – Vyas Bharghava Apr 15 '09 at 00:36
  • And hey Jonathan, I do appreciate you taking time out to help. Not an ungrateful moron, you see... :) – Vyas Bharghava Apr 15 '09 at 00:37
  • Could you paste in more of your code then? The code I posted worked for me, and that's the only thing I can think of that would make it work like you describe. – Jonathan Rupp Apr 15 '09 at 00:39
  • The point is the following: "[...]Single().Value = value.Value;" just updates the value of the property but does not replace the property object in the array with the supplied one. This would require something like "[...]Single() = value;". Obvisiously this does not work - LINQ does not support – Daniel Brückner Apr 15 '09 at 01:03
  • modifying collection. You have to build a new one and replace the old one with that - see the example in my answer - but this is not a smart solution. – Daniel Brückner Apr 15 '09 at 01:05
  • Ah, I see what the real question is. Yes, you cannot modify the collection with LINQ. Language integrated **query**, not update. – Jonathan Rupp Apr 15 '09 at 01:32
  • I understand I can't use Linq as 'lvalue'... but can I at least get the index of the item in the collection? If I could, I could modify the original collection using the index. Thanks again danbruc & Jon. – Vyas Bharghava Apr 16 '09 at 00:31
  • Well, there's List.FindIndex, but that's not available on Array (and I'm guessing the .ToList() would kind of defeat the purpose). That said, you should be able to write a custom extension method to implement FindIndex on a array if you really want to. – Jonathan Rupp Apr 16 '09 at 06:43