Я работаю в Javascript и создаю автономное приложение.
У меня очень большой массив, более 10 МБ. Мне нужно сохранить элементы из массива, где пользователь внес изменения, но запись/чтение массива на диск происходит слишком медленно.
Я хотел бы создать второй массив, который содержит только изменения, сделанные пользователем, поэтому я могу сохранить этот меньший массив, а затем, когда мне нужно загрузить изменения, он может отразить свои изменения в большом массиве.
Например:
lib[x][1][y][6] = "no";
libVars[x][1][y][6] = "no";
libVars будет просто хранить изменения элементов в lib и не будет иметь тот же размер, что и большой массив lib, поскольку пользователь будет взаимодействовать только с небольшой частью большого массива.
Очевидно, это не работает, так как libVars не имеет той же структуры, что и lib, а если бы она была, это также заняло бы много памяти (я так думаю в любом случае!)
Затем я хотел бы перебрать libVars и обновить lib в тех же точках элементов, где изменения были сохранены в libVars.
Есть ли способ сохранить какой-либо указатель на место в lib, которое я мог бы сохранить в libVars с соответствующим значением элемента?
Любая помощь приветствуется.
Спасибо.
======================
Образец моего большого массива
var lib = [
[241,[[221119,"sample data","sample","no","no",131,"no"],
[221121,"sample2 data","sample2","no","no",146,"no"],
[221123,"sample3 data","sample3","no","no",28,"no"],
[221626,"sample4 data","sample4","no","no",26,"no"],
[221628,"sample5 data","sample5","no","no",88,"no"]]],
[330,[[305410,"2sample data","sample 2b","no","no",197,"no"],
[305412,"2sample 2 data","sample2 2b","no","no",147,"no"],
[305414,"3sample 2 data","sample3 2b","no","no",10,"no"] ...
Попытка решения, опубликованного Z-Bone
Я добавил переменную tempLib, но не вижу, как вы в конечном итоге обновите фактический объект lib
Object.entries(libVars).forEach(([path, value]) => {
var tempLib = lib;
var pathParts = path.split('#');
pathParts.forEach((pathPart, index) => {
if (index == pathParts.length-1) {
tempLib[pathPart] = value;
} else {
//I don't fully track what you are doing here.
//I assume you are building the array path, but to me it looks like
// it's getting wiped out and not built upon?
tempLib = tempLib [pathPart];
}
});
});
@JoeWarner Спасибо, но я бы хотел избежать каких-либо библиотек.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Обновление 28.01.2019 На основе данных испытаний nomaam
@nomaam Пожалуйста, проверьте следующий фрагмент. Я заставил изменить произвольный путь в массиве lib. Путь я выбрал такой: lib[0][1][3][4].
Вы можете видеть, как он сначала возвращает значение "no", а затем, после выполнения решения по отслеживанию модификаций, возвращает значение "yes".
// First Step: Original Data (lib)
var lib = [
[241,[[221119,"sample data","sample","no","no",131,"no"],
[221121,"sample2 data","sample2","no","no",146,"no"],
[221123,"sample3 data","sample3","no","no",28,"no"],
[221626,"sample4 data","sample4","no","no",26,"no"],
[221628,"sample5 data","sample5","no","no",88,"no"]]],
[330,[[305410,"2sample data","sample 2b","no","no",197,"no"],
[305412,"2sample 2 data","sample2 2b","no","no",147,"no"],
[305414,"3sample 2 data","sample3 2b","no","no",10,"no"]]]]
// Log initial value in arbitrary selection lib[0][1][3][4]
console.info(lib[0][1][3][4])
// Second Step: Pointer's object
var libVars = {}
// Example of changing a value via an artificial "pointer" to lib[0][1][3][4]
libVars['0#1#3#4'] = 'yes'; // original value is "no"
// Third Step: Run the modifier - update changes from libVars to lib
Object.entries(libVars).forEach(([path, value]) => {
var tempLib = lib;
var pathParts = path.split('#');
pathParts.forEach((pathPart, index) => {
if (index == pathParts.length - 1) {
tempLib[pathPart] = value;
} else {
tempLib = tempLib[pathPart];
}
});
});
// Log the change fro lib
console.info(lib[0][1][3][4])Оригинальный ответ
Если я правильно понимаю вашу проблему, технически вы можете хранить объект с путем к свойству, которое вы хотите изменить, в качестве ключа и измененного значения в качестве его значения.
скажем, ваш исходный объект выглядит так:
var lib = {a: {b: { c: { d: "yes"}}}}
Вы можете вести журнал изменений в объекте резервного копирования (например, изменение значения с «да» на «нет»).
Это может выглядеть примерно так, если . помечает вложенное свойство.
var libVars = {};
libVars['a.b.c.d'] = "no";
Затем, когда вы хотите обновить исходный большой массив/объект значений, вы можете сделать это:
Object.entries(libVars).forEach(([path, value]) => {
var tempLib = lib;
// split by nesting indicator -> dot
var pathParts = path.split('.');
// iterate all parts of the path to the modified value
pathParts.forEach((pathPart, index) => {
if (index == pathParts.length-1) {
// once you made your way to last index, update value
tempLib[pathPart] = value;
} else {
tempLib = tempLib[pathPart];
}
})
})
console.info(lib); // outputs => {a: {b:{c: {d : "no"}}}}
Разве это не предполагает, что в имени ключа не может быть .? например {'a.b': {'c.d': 'yes'}}
@customcommander Это пример возможного подхода. Вы можете заменить точку представления вложения любым другим символом. Вы также можете избежать его, а затем разделить с помощью оператора экранирования.
Я пытаюсь заставить ваш пример работать. Я получаю неопределенные ошибки в разделе tempLib = tempLib[pathPart]. Мне нравится концепция. Не могли бы вы уточнить для меня, если я обычно обращаюсь к массиву, такому как lib[0][1][3][6], есть ли способ преобразовать строку без цикла, например lib[ "0,1,3, 6" ], если это возможно, то можно было бы просто вставить прямые пути к "указателям", и мне не пришлось бы беспокоиться о неопределенных ошибках или проблемах с индексацией в цикле.
Я опубликовал свою попытку вашего решения с комментариями в моем исходном сообщении.
@nomaam Первое, что я вижу, это то, что вы используете исходную библиотеку, чтобы добраться до конечного значения (каждый раз присваивая ее себе). Таким образом вы потеряете оригинальный lib. В каждой итерации используйте временную переменную, для которой установлено значение lib, и повторяйте ее. (см. мой пример: var tempLib = lib;). Было бы очень полезно, если бы вы также могли опубликовать частичный пример вашего массива/объекта lib. Обратите внимание, что в моем примере я использовал объект, а не массив.
Я добавил короткий пример моего массива lib. Также я обновил свой скрипт, чтобы иметь tempLib, однако именно здесь я сталкиваюсь с неопределенными ошибками, как я упоминал в своих последних комментариях. Где вы на самом деле обновляете объект master lib с изменениями?
Привет @nomaam, пожалуйста, ознакомься с моим обновленным ответом. Он содержит фрагмент, показывающий полный рабочий цикл с предоставленным вами набором данных. Удачи :)
Обновление 2
Обновил мой ответ, чтобы избавиться от lodash, и он оказался равным ответу Z-Bone. Его вариант еще более понятен, чем мой.
Обновлено @ 28.01.2019
var lib = [
[241,[[221119,"sample data","sample","no","no",131,"no"],
[221121,"sample2 data","sample2","no","no",146,"no"],
[221123,"sample3 data","sample3","no","no",28,"no"],
[221626,"sample4 data","sample4","no","no",26,"no"],
[221628,"sample5 data","sample5","no","no",88,"no"]]],
[330,[[305410,"2sample data","sample 2b","no","no",197,"no"],
[305412,"2sample 2 data","sample2 2b","no","no",147,"no"],
[305414,"3sample 2 data","sample3 2b","no","no",10,"no"]]]];
// object to track edits
var edits = {
'0.1.2.2': 'edited'
};
// sample/simple implementation of lodash's 'set' function
function applyEdit(arr, path, value) {
var items = path.split('.');
var last = items.length - 1;
var current = arr;
items.forEach(function (item, i) {
if (i !== last) {
current = current[item];
}
});
current[items[last]] = value;
}
// our restoration function
function patch(arr, edits) {
Object.keys(edits).forEach(function(key) {
var newValue = edits[key];
applyEdit(arr, key, newValue);
});
}
console.info(lib[0][1][2][2]) // "sample3"
// now we can restore our edits
patch(lib, edits);
console.info(lib[0][1][2][2]) // "edited"
// --------------------------------------------------------
// to prevent forgetting to track the updates you can use utility function
// to make changes to your 'lib' array
function update(arr, path, value) {
applyEdit(arr, path, value);
edits[path] = value;
}
Оригинальный ответ:
Одним из возможных решений было бы иметь объект, в котором вы отслеживаете все изменения.
const edits = {}; И когда вы обновляете свой объект с помощью lib[x][1][y][6] = "no";, вы также сохраняете свое редактирование:
edits[`${x}.1.${y}.6`] = "no";
В основном вы просто создаете строку, содержащую путь изменения.
После того, как вы загрузите свой объект редактирования из файла, вы можете применить все изменения к исходному массиву с помощью следующего кода:
import { set } from 'lodash';
function patch(arr, edits) {
Object.keys(edits).forEach(key => {
const newValue = edits[key];
set(arr, key, newValue);
});
}
Спасибо вам за публикацию. Это похоже на идею @Z-Bone. Просто пытаюсь заставить его работать на моем конце. Смотрите мои последующие сообщения, прикрепленные к его решению.
Я пытаюсь протестировать ваше решение, но не могу использовать внешние библиотеки, такие как lodash.
Предполагая, что пути в libVars такие же, как и в lib, вы можете просто записать изменения в массив функций, которые вы повторно примените к своему объекту lib позже. :
const data = {foo: 'aaa', bar: 'bbb'};
console.info('data before:', data);
const updates = [];
updates.push(obj => {
obj.foo = 'foo';
});
updates.push(obj => {
obj.bar = 'bar';
});
updates.forEach(fn => fn(data));
console.info('data after:', data);Спасибо, но я думаю, что это вызовет проблему, о которой я упоминал в вопросе, что меньший массив, содержащий изменения, будет в значительной степени иметь размер, аналогичный основному массиву, поскольку пустые значения массива все еще занимают память.
Единственное, что updates будет содержать, это функции, а не фактические данные, с которыми они работают.
Это можно решить с помощью эмуляции автовивификации с использованием прокси. Видеть: