Object values not changing - javascript

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`)
}

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

Change Object Index

Is there way to change the index dynamically? or rebuild this object to where the 1 will be the Id of what ever object get passed into the function? Hope this makes sense.
export const createTree = (parentObj) => {
//keep in memory reference for later
const flatlist = { 1: parentObj }; <---- change the 1 based on parent.Id
...
}
My attempt thinking it would be easy as:
const flatlist = { parentObj.Id: parentObj };
Use computed property names to create a key from an expression:
const createTree = (parentObj) => {
const flatlist = { [parentObj.id]: parentObj };
return flatlist;
}
console.log(createTree({ id: 1 }));

Overriding an array function for a single instance

I'm trying to write a custom push function and only override it for a single instance. I'm trying to wire it up so every time I push to this array I can trigger my extra code. I don't want to override it for all Arrays or create a custom class if I don't have to.
First I had this:
let user_progress = {completed: [], inprogress: [] }
user_progress.completed.push = (item) => {
Array.prototype.push.call(user_progress.completed, item)
//do extra stuff
common.store_data(user_progress, RESOURCE_COLLECTION)
return undefined;
}
Then I tried this:
let Progress = function(){
this.completed = new Array()
this.inprogress = new Array()
}
let user_progress = new Progress()
user_progress.completed.push = (item) => {
Array.prototype.push.call(user_progress.completed, item)
common.store_data(user_progress, RESOURCE_COLLECTION)
return undefined;
}
user_progress.completed.push(item) works the first time you call it then it gets overwritten and becomes the standard Array.push. I assume it has something today with the Array.Prototype.push.call() and passing in user_progress.completed. What am i doing wrong? Both don't work and have the same issue. What is going on in the background here and is there an easy fix?

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