Перезапись циклов | JS
Есть массив: arr = [{}, {}, {}]
Мне нужно, чтобы в каждый объект массива, не зависимо от того, массив arr ли это или созданный только что, записывалось значение его индекса в массиве. Т.е. если я куда-то передам arr[2] и оттуда вызову arrayChild.myIndex, то мне выдаст индекс этого элемента в массиве.
Я думал делать это через Object.defineProperty, но вообще не понимаю, как такое реализовать.
Object.defineProperty(Array.prototype, ???).
Может как-то переопределять forEach/map/reduce и т.д.? Звучит как плохой вариант.
Ожидаемый результат:
До внедрения:
arr = [{}, {}, {}]
arr.map(v => v.myIndex) -> error
После внедрения скрипта:
arr = [{}, {}, {}]
arr.map(v => v.myIndex) -> [0, 1, 2]
UPD:
const arr = [{id: 1}, {id: 2}, {id: 3}, {id: 4}];
arr.forEach((item, i) => {
Object.defineProperty(item, "myIndex", {
get: function (){
return i;
}
});
});
console.log("__", arr.map(item => ({
current: item,
index: item.myIndex
})));
Это работающий пример. Но я четко в вопросе написал, как мне сделать это на фундоментальном уровня (на уровне прототипа). Чтобы не делать для каждого массива forEach/map/и т.д.
Ответы (2 шт):
Через forEach делается очень просто)
arr.forEach((el, i) => {el.myIndex = i})
- "el" - очередной элемент в массиве.
- "i" - индекс этого элемента.
- "el.myIndex = i" - присваиваем элементу свойство myIndex со значением его индекса.
То, что я пытался сделать не очень хорошо. Почитав статьи в интернете я понял, что таким методом можно сломать вообще весь js на странице. Поэтому я немного поменял условия и добился того, чего хотел:
const defineItem = (item, arr, index) => { //Чтобы не дублировать код
if (!item?.prev) {
Object.defineProperty(item, "prev", {//Объявляем ключ и устанавливаем только геттер
get: () => {
if (arr[index - 1]) {
return arr[index - 1];
}
return null;
}
});
}
if (!item?.next) {
Object.defineProperty(item, "next", {
get: () => {
if (arr[index + 1]) {
return arr[index + 1];
}
return null;
}
});
}
}
//Объявляем новую функцию-итератор
Array.prototype.smartForEach = function (callback) {
this.forEach((item, index, arr) => { //запускаем стандартный forEach
defineItem(item, this, index); //определяем для каждого итема в нём новые переменные
callback(item, index, arr); //вызываем коллбек
}, this);
} // То же самое для другой функции
Array.prototype.smartMap = function (callback, thisArgs) {
return this.map((item, index, arr) => {
defineItem(item, this, index);
return callback(item, index, arr);
});
}
Теперь в любой точке кода можно создавать или использовать уже созданные массивы по новому:
Объяснять для чего это не вижу смысла, т.к. все, кто занимался группировкой больших массивов объектов (с большим количеством ключей, которые нужно учитывать при группировке), сочтут полезным знать в каждой итерации соседние элементы.
Пример такой группировки:
const groupArr = [
{
isThread: false,
userID: 0,
created_at: 0
},
{
isThread: false,
userID: 1,
created_at: 1
},
{
isThread: false,
userID: 1,
created_at: 2
},
{
isThread: true,
userID: 0,
created_at: 3
},
{
isThread: false,
userID: 0,
created_at: 4
},
{
isThread: false,
userID: 3,
created_at: 18
},
{
isThread: false,
userID: 0,
created_at: 19
},
{
isThread: false,
userID: 4,
created_at: 41
},
{
isThread: false,
userID: 0,
created_at: 42
},
{
isThread: true,
userID: 0,
created_at: 51
},
{
isThread: false,
userID: 0,
created_at: 61
},
{
isThread: false,
userID: 0,
created_at: 71
},
{
isThread: false,
userID: 0,
created_at: 72
}
]
let group = 0;
console.log('_', groupArr.smartMap((item, index) => {
if (item.prev) {
let isNewGroup = false;
if (!isNewGroup) isNewGroup = item.prev.userID !== item.userID;
if (!isNewGroup) isNewGroup = item.prev.isThread !== item.isThread;
if (!isNewGroup) isNewGroup = (item.created_at - item.prev.created_at) > 10;
if (isNewGroup) {
group += 1;
}
}
return {
group: group,
...item
}
}));
И получаем идеальный результат.
Особенно это полезно, когда элемент передается в функцию (React Component к примеру), но не хочется прокидывать весь массив для группировки внутри.


