24

How do you replace a given index in an observableArray with another element.

I have: ViewModel.Elements()[index]

how can i replace it with another element.

5 Answers 5

38

observableArrays do have a replace method. This takes in the old item and the new item.

So, you would call it like:

ViewModel.Elements.replace(ViewModel.Elements()[index], yourNewElement);

Internally, this just sets that index to your new item and calls valueHasMutated() to notify any potential subscribers.

Sign up to request clarification or add additional context in comments.

3 Comments

You will probably need to make yourNewElement observable. eg. ko.mapping.fromJS(yourNewElement).
Yes, we just don't have API docs for the various methods on observableArrays at the moment.
I've searched the KnockoutJS site for documentation on the replace function of observable arrays, but I can't find anything. Am I missing it? Can someone point to where I can find info on this function?
10

It's a little late by now, but I have noticed that this question is still left unanswered (properly).

As the others have stated, the 1st answer does not give the expected behavior, it will replace an item of an observableArray by value and not by index!

The other answers are overly complicated and does not take advantage of Knockout's extended natives.


To truly replace a value in a given index of an observableArray, you should use the splice() method.

var myObservable = ko.observableArray([1, 2, 3, 4]);
myObservable.splice(0, 1, 100); // Replace the value at [0] with "100"
myObservable.splice(2, 1, 300); // Replace the value at [2] with "300"
console.log(myObservable()); // [100, 2, 300, 4]

And since the splice() method is extended by Knockout, it will automatically notify any dependencies, eliminating the need to call valueHasMutated().

EDIT / February 14th 2016

In case you need to replace multiple values at once, it'll be much quicker to get the underlying array, perform the replacement and then notify changes.

var myObservable = ko.observableArray([1, 2, 3, 4]);
var underlyingArray = myObservable();

myObservable.valueWillMutate();

for(...)
   underlyingArray[i] = newValue[i];

this.valueHasMutated();

1 Comment

I think the splice way is a better answer. Using the value to replace or remove is begging for problems. Especially where you need to keep the order of things and there happens to be 2 values the same in different locations
5

Like dvijaz correctly points out, the accepted answer replaces a given value. This can cause problems when the given value is not unique in the array.

Instead of slicing the array and then gluing the array back together, I did the following which is more readable IMHO:

ViewModel.Elements()[index] = yourNewElement;
ViewModel.Elements.valueHasMutated();

Thanks to RP Niemeyer for pointing out how .replace works internally.

You can also extend observableArray to support this:

ko.observableArray.fn.replaceIndex = function(index, newValue) {
    this.valueWillMutate();
    this()[index] = newValue;
    this.valueHasMutated();
};

Which means you can do the following:

var arr = ko.observableArray([1, 2, 1, 4]);
arr.replaceIndex(2, 3); 
console.log(arr()); // logs [1, 2, 3, 4]

Comments

4

The question asks how to replace "a given index", rather than a given value. The accepted answer works when there are no repetitions in the observableArray, but in case there are repetitions then it will not always behave as expected. E.g. suppose we have:

index = 2;
elements = ko.observableArray([9,8,9]);

and then we use the accepted answer.

elements.replace(elements()[index], 7);

Then elements will be [7,8,9] (because replace uses indexOf, which finds the lowest index of the given value 9), whereas the question surely expects a solution that would make elements be [9,8,7].

To truly replace with newElement the item at index in an observableArray elements, you can use the following.

elements(elements.slice(0, index).concat(newElement, elements.slice(index + 1)));

Something more elegant would be nice. Though IIUC it's a question of readability rather than of performance.

Comments

0

Editing the underlying array and reassigning it did the trick for me. I had trouble with other approaches.

var underlyingArray = this.someObservableArray();

// Do modifications... Swap some items...
// No one is being notified just yet...

// Reassign to observable to update everything
this.someObservableArray(underlyingArray);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.