get all localstorage items in order they were setted in - javascript

I want to know if there is a way to iterate through all of localstorage data in the order that they were putted in. The problem is that my keys are the id of the object and will not be in order.
I could have an item with the id 102 and the next one would be 3. I need to keep those id's because all of tr are identified with those id so that i can manipulate them so i need the localstorage.setitem(ID,data) to stay like this.
Is there a way to kind of like a push/pop the localstorage or like a localStorage.First() so that i can get my items in order ?

You are think about localStorage as array, but localStorage is a hash. Create your own variable in localStorage, and implement two methods: push and pop
class LocalStorageStack {
constructor(stackName) {
this.stackName = stackName
}
push = (value) => {
const stack = this.stack
stack.push(value)
this.stack = stack
}
pop = () => {
const stack = this.stack
const value = stack.pop()
this.stack = stack
return value
}
get stack() {
return (window.localStorage[this.stackName] || '').split(',')
}
set stack(values) {
window.localStorage[this.stackName] = values.join(',')
}
}
const localStorageStack = new LocalStorageStack('myStack')
localStorageStack.push('111')
localStorageStack.push('112')
You can see in localStorage.myStack string variable == '111,112' and can pop values in backward order:
localStorageStack.pop()
=> "112"
localStorageStack.pop()
=> "111"
const windowLocalStorage = {}
class LocalStorageStack {
constructor(stackName) {
this.stackName = stackName
}
push = (value) => {
const stack = this.stack
stack.push(value)
this.stack = stack
}
pop = () => {
const stack = this.stack
const value = stack.pop()
this.stack = stack
return value
}
get stack() {
// Sorry, in the snippet localStorage is disabled by security reasons.
// In this example local variable `windowLocalStorage emulates them
return (windowLocalStorage[this.stackName] || '').split(',')
}
set stack(values) {
windowLocalStorage[this.stackName] = values.join(',')
}
}
const localStorageStack = new LocalStorageStack('my-stack')
console.log('push 111')
localStorageStack.push('111')
console.log('push 112')
localStorageStack.push('112')
console.log(localStorageStack.pop())
console.log(localStorageStack.pop())

Related

filtering an array issue

I'm importing an array into a module, and adding and removing items from that array. when I give a push, it adds the item to the array globally, so much so that if I use that same array in another module, it will include this item that I pushed. but when I try to filter, with that same array getting itself with the filter, it only removes in that specific module. How can I make it modify globally?
let { ignore } = require('../utils/handleIgnore');
const questions = require('./quesiton');
const AgendarCollector = async (client, message) => {
ignore.push(message.from);
let counter = 0;
const filter = (m) => m.from === message.from;
const collector = client.createMessageCollector(message.from, filter, {
max: 4,
time: 1000 * 60,
});
await client.sendText(message.from, questions[counter++]);
collector.on('start', () => {});
await collector.on('collect', async (m) => {
if (m) {
if (counter < questions.length) {
await client.sendText(message.from, questions[counter++]);
}
}
});
await collector.on('end', async (all) => {
ignore = ignore.filter((ignored) => ignored !== message.from);
console.log(ignore);
const finished = [];
if (all.size < questions) {
console.log('não terminado');
}
await all.forEach((one) => finished.push(` ${one.content}`));
await client.sendText(message.from, `${finished}.\nConfirma?`);
});
};
module.exports = AgendarCollector;
see, in this code, import the ignore array and i push an item to then when the code starts and remove when its end.
but the item continues when I check that same array in another module.
I tried to change this array ignore by using functions inside this module but still not working
let ignore = [];
const addIgnore = (message) => {
ignore.push(message.from);
};
const removeIgnore = (message) => {
ignore = ignore.filter((ignored) => ignored !== message.from);
console.log(ignore);
};
console.log(ignore);
module.exports = { ignore, addIgnore, removeIgnore };
You are using the variables for import and export and hence cought up with issues.
Instead, make use of getters.
Write a function which will return the array of ignore. something like this:
const getIgnoredList = () => {
return ignore;
};
and in your first code, import getIgnoredList and replace ignore with getIgnoredList()
Explanation :
Whenever we import the variables only the value at that particular time will be imported and there will not be any data binding. Hence there won't be any change in the data even though you think you are updating the actual data.
When you use require(...) statement it's executed only once. Hence when you try to access the property it gives the same value everytime.
Instead you should use getters
let data = {
ignore : [],
get getIgnore() {
return this.ignore
}
}
module.export = {
getIgnore: data.getIgnore,
}
Then wherever you want to access ignore do
var {getIgnore}= require('FILE_NAME')
Now: console.log(getIgnore) will invoke the getter and give you current value of ignore
Using getters will allow you to access particular variables from other modules but if you want to make changes in value of those variables from other module you have to use setter.
More about getters here
More about setters here

Object values not changing

I'm working on a VueJS component that, among other things, can export data to .xlsx. For this, I'm using the json2xls library - so I need to pass to the json2xls() function an array of objects with identical keys (said keys will be column names)
This data I have to export is in an array of pretty deeply nested objects, though, so I need a function that will process that data to a form that will work with json2xls.
This is the method I'm using for that:
exportReport () {
const dataMap = []
this.reportPreview.forEach(elm => {
const auxObj = {}
auxObj.name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
dataMap.push(auxObj)
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}
If I do this, however, it appears that in cycles of elm.legal_files.forEach() the properties auxObj.legalfile and auxObj.actions are not overwritten, pushing several objects with identical values to dataMap
Why is this happening? What am I doing wrong? I'm hacking my way around this copying auxObj after "overwriting" the legalfile and actions properties and pushing the copy. This hack works, but I wonder what causes the first behavior and if there's a cleaner way around it.
exportReport () {
const dataMap = []
this.reportPreview.forEach(elm => {
const auxObj = {}
auxObj.name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
/*
If I just push auxObj to dataMap, the object is pushed with the same properties every time.
Copying auxObj and pushing the copy is a hack around this problem, but there may be a cleaner solution.
*/
const objCopy = { ...auxObj }
dataMap.push(objCopy)
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}
You pushed the same object every time.
exportReport() {
const dataMap = []
this.reportPreview.forEach(elm => {
const name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
const auxObj = {} // Create a new object here
auxObj.name = name
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
dataMap.push(auxObj) // Push it into the array
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}

JSON is being accessed every time even with an invalid key

So I have a function that has an JSON within it, and it's value consists in a key-pair in which the key is recieved as parameter and the value is the return of another function, like shown below.
const normalizeKeyValuePair = (key, value) => {
const propertyHandler = {
speed: normalizeSpeed(value),
actions: normalizeActions(value)
};
return [normalizeField(key), propertyHandler[key] || normalizeValue(value)];
};
The problem is with the actions key. Every single key parameter that normalizeKeyValuePair recieves is thrown into actions and goes to normalizeActions. How can I prevent this from happen?
To understand why this is a problem, this is normalizeActions. When actions is an primitive, JS throws an error.
const normalizeActions = actions => {
const normalizedActions = [];
for(let action of actions) {
normalizedActions.push([action.name, action.desc]);
}
return normalizedActions;
}
Thanks in advance. Let me know if needs more information!
Every time normalizeKeyValuePair is invoked, it will call normalizeActions(value) when it creates propertyHandler.
This should do what you intended:
const propertyHandler = {
speed: normalizeSpeed,
actions: normalizeActions
};
const normalizeKeyValuePair = (key, value) => {
const ph = propertyHandler[key];
return [normalizeField(key), (ph && ph(value)) || normalizeValue(value)];
};
It seems like the logic is all wrong. You should check key before calling the normalize functions, and only call the appropriate one.
const normalizeValuePair(key, value) {
let normalKey = normalizeField(key);
let normalValue;
switch(value) {
case 'speed':
normalValue = normalizeSpeed(value);
break;
case 'actions':
normalValue = normvalizeActions(value);
break;
default:
normalValue = normalizeValue(value);
}
return [normalKey, normalValue];
}

Updating State React

The following image represents an object with two ui controls that are stored as this.state.controls
Initially the statesValue values are set via data that is received prior to componentDidMount and all is good. However updates to the each of the controls statesValues are sent via an event , which is handled with the following function
const handleValueStateChange = event => {
let controls = Object.entries(this.state.controls);
for (let cont of controls) {
let states = cont[1].states;
if (states) {
let state = Object.entries(states);
for (let [stateId, contUuid] of state) {
if (contUuid === event.uuid) {
cont[1].statesValue[stateId] = event.value;
}
}
}
}
};
which updates the values happily, however bearing in mind the updated values that change are a subset of this.state.controls, I have no idea how to use this.setState to update that that has been changed.
thanks in advance for any pointers
Instead of using Object.entries try destructuring to keep the reference to the objects.
And have a look at lodash. There are some nice helper functions to iterate over objects like mapValues and mapKeys. So you can keep the object structure and just replace the certain part. Afterwards update the whole state-object with the new one.
const handleValueStateChange = event => {
let {controls} = this.state;
controls = _.mapValues(controls, (cont) => {
const states = cont[1].states;
if (states) {
_.mapValues(states, (contUuid,stateId) => {
if (contUuid === event.uuid) {
cont[1].statesValue[stateId] = event.value;
}
});
}
return cont;
});
this.setState({controls});
};
Code is not tested but it should work like this.
Problem is you're updating an object which you've changed from it's original structure (by using Object.entries). You can still iterate in the same way however you'll need to update an object that maintains the original structure. Try this:
Make a copy of the controls object. Update that object. Replace it in state.
const handleValueStateChange = event => {
// copy controls object
const { controls } = this.state;
let _controls = Object.entries(controls);
for (let cont of _controls) {
let states = cont[1].states;
if (states) {
let state = Object.entries(states);
for (let [stateId, contUuid] of state) {
if (contUuid === event.uuid) {
// update controls object
controls[cont[0]].statesValue[stateId] = event.value;
}
}
}
}
}
// replace object in state
this.setState({controls});
};

Having one list of items which is shown few times, is there any technique which let to sort each representation independently?

I have one list\source of tracks, which is shown few times on different pages. I want to allow users to sort this list independently (per page).
I could create few copies of this list, but the problem is that this list can be changed (add\remove\rename tracks and so on), so in this case few copies of this list seems to be not a good solution, because such changes must be applied to all copies of the list.
Is there any technique which allows to sort tracks independently, having just one source of tracks?
Demo
You got basically two options:
1) Notify all components that got their own sorted version of the list when the list changes, so that they can copy the changed list and sort / display it. For that you need a way of emitting events, e.g. as a mixin class:
const EventEmitter = Parent => class EventEmitter extends Parent {
constructor(...args) {
super(...args);
this._emitters = {};
}
trigger(evt, ...args) {
(this._emitters[evt] || []).forEach(emitter => emitter(...args));
}
on(evt, handler) {
(this._emitters[evt] || (this._emitters[evt] = [])).push(handler);
}
};
Then you can proxy an array, so that on every change an update event is triggered:
const ChangeEmitter = Parent => {
const Mixin = EventEmitter(Parent);
return (...args) => new Proxy(
new Mixin(...args),
{
set(obj, prop, value) {
const result = Reflect.set(...arguments);
obj.trigger("update", obj);
return result;
}
}
);
};
That looks very complex, but now we can do this:
// Our source of truth:
const base = new ChangeEmitter(Array);
// One of the functions that renders the data:
function renderSorted(array) {
const sorted = array.sort();
document.body.innerHTML = sorted;
}
renderSorted(base); // Initial render
base.on("update", renderSorted);
base[0] = 1;
So whenever we update the base array, all renderers will get called again and everything can get updated.
2) Create a few sorted arrays and apply every mutation to all of them. For that we need a sorted array:
class SortedArray extends Array {
constructor(sorter, ...args) {
super(...args);
this.sorter = sorter;
this.sort(sorter);
}
// Do insertion sort
add(el) {
let index = 0;
while(index <= this.length && this.sorter(el, this[index]) > 0) index++;
this splice(index, 0, el);
}
remove(el) {
this.splice(this.indexOf(el), 1);
}
}
And a way to change multiple arrays in parallel:
const unifyArrays = (...arrays) => ({
add(el) { arrays.forEach(arr => arr.add(el)); },
remove(el) { arrays.forEach(arr => arr.remove(el)); }
});
So now you can do:
const sorted = new SortedArray((a, b) => a - b);
const reversed = new SortedArray((a, b) => b - a);
const base = unifyArrays(sorted, reversed);
base.add(1);
For sure you can combine both approaches, so that evenrs will be emitted when the arrays get changed.

Categories