Reputation: 2386
Thank you all for your inputs, thanks to you I realize now I've made a seriously wrong assumption about JS. Actually, the code provided below works fine, if one modifies it:
...
let currentPlaceInArray;
for (let i = 0; i < position.length; i++) {
let place = position[i];
if (i + 1 === position.length) return currentPlaceInArray[place] = content; // Added this line now
/***Explanation:
Without this added line, the currentPlaceInArray variable is reassigned in the end
to 'needle' in the present example. Thus, assigning it to something else (like
currentPlaceInArray = ['updated']) will not have any effect on the array, because
currentPlaceInArray is not assigned to it anymore, it's assigned to the string 'needle'
***/
currentPlaceInArray = currentPlaceInArray ? currentPlaceInArray[place] : myArray[place];
}
I have a large unstructured multidimensional array, I do not know how many nested arrays it has and they have different lengths, like in this simplified example:
var myArray =
[
[
[],[]
],
[
[],[],[],[]
],
[
[],
[
[
['needle']
]
]
]
];
I want to be able to update 'needle'
to something else. This would be possible by executing myArray[2][1][0][0] = 'updated';
I want to create a function which takes as parameters just 2 pieces of information: 1) the string to be written and 2) an array with the position of the array item to be updated. In the above case of myArray
, it would be called thus: changeNeedle('updated', [2, 1, 0, 0])
.
However, I can't assign a variable to an array key, just to its value. If it was possible to assign a variable to the array key, I could just update that variable with the current position (i.e., [Update: it's the exact opposite, assigning a variable to an array will point exactly to that array, so var currentPosition
would be myArray[x]
, and currentPosition[y]
would then be myArray[x][y]
)currentPosition[y] === myArray[x][y]
]. Like so:
function changeNeedle(content, position) {
/***
content: a string
position: an array, with the position of the item in myArray to be updated
***/
let currentPlaceInArray;
for (let i = 0; i < position.length; i++) {
let place = position[i];
currentPlaceInArray = currentPlaceInArray ? currentPlaceInArray[place] : myArray[place];
}
currentPlaceInArray = content;
}
Is it possible to implement this changeNeedle
function without using eval
, window.Function() or adding a prototype to an array?
Upvotes: 0
Views: 573
Reputation: 836
At first it looked like Chase's answer was kind of right (using recursion), but it seems you want your code to follow only the path provided to the function.
If you are certain of the position of the element you want to change, you can still use a recursive approach:
var myArray = [
[
[],
[]
],
[
[],
[],
[],
[]
],
[
[],
[
[
['needle']
]
]
]
];
function changeNeedle(arr, content, position) {
// removes first element of position and assign to curPos
let curPos = position.shift();
// optional: throw error if position has nothing
// it will throw an undefined error anyway, so it might be a good idea
if (!arr[curPos])
throw new Error("Nothing in specified position");
if (!position.length) {
// finished iterating through positions, so populate it
arr[curPos] = content;
} else {
// passes the new level array and remaining position steps
changeNeedle(arr[curPos], content, position);
}
}
// alters the specified position
changeNeedle(myArray, 'I just got changed', [2, 1, 0, 0]);
console.log(myArray);
However, if you want to search for the element that corresponds to the content, you still have to iterate through every element.
Upvotes: 1
Reputation: 3126
If you'd like to avoid importing a library, a array-specific solution with in-place updates would look something like:
function changeNeedle(searchValue, replaceWith, inputArray=myArray) {
inputArray.forEach((v, idx) => {
if (v === searchValue) {
inputArray[idx] = replaceWith;
} else if (Array.isArray(v)) {
changeNeedle(searchValue, replaceWith, v);
}
});
return inputArray;
}
Then you can call changeNeedle('needle', 'pickle', myArray);
and your myArray
value will be mutated to change instances of needle
to pickle
.
Return value and myArray
would then look like:
JSON.stringify(myArray, null, 4);
[
[
[],
[]
],
[
[],
[],
[],
[]
],
[
[],
[
[
[
"pickle"
]
]
]
]
]
Update: Based on your reply, a non-recursive solution that follows the exact path of a known update.
function changeNeedle(newValue, atPositions = [0], mutateArray = myArray) {
let target = mutateArray;
atPositions.forEach((targetPos, idx) => {
if (idx >= atPositions.length - 1) {
target[targetPos] = newValue;
} else {
target = target[targetPos];
}
});
return mutateArray;
}
See: https://jsfiddle.net/y0365dng/
Upvotes: 1
Reputation: 990
No, You can't.
The simplest way to do this is to store the array which you are going to modify (array = myArray[2][1][0]
) and the key (index = 0
). Then you just need to make a regular assignment like array[index] = value
.
You can consider the following implementation:
function changeNeedle(value, keys) {
let target = array;
for (const i of keys.slice(0, -1)) {
target = target[i];
}
target[keys[keys.length - 1]] = value;
}
Upvotes: 0
Reputation: 7294
I suggest to consider the well-tested Lodash function update (or set, and so on). I think that's very close to what you need.
Upvotes: 1