Trying to deep clone an array of objects leaves out elements - javascript

This is in react/redux, below is the buggy case that executes in my reducer:
case actionTypes.UPDATE_PROJECT:
const projectArr = JSON.parse(JSON.stringify(state.projects));
console.log(projectArr);
console.log("PROJECTS BEFORE UPDATE: ", projectArr, state.projects);
state.projects is an object array, e.g.
projects: [{projName: "proj1"}, {projName: "proj2"}]
The thing is, if my state.projects was like the above, it somehow doesn't deep clone properly into projectArr. When I do console.log(projectArr), the output is [{projName: "proj1"}, {}].
When I do console.log("PROJECTS BEFORE UPDATE: ", projectArr, state.projects), I get the full array (ie [{projName: "proj1"}, {projName: "proj2"}]).
Any hunches? I know that this is kind of sparse info but it's nested this rabbit hole code crap that would take pretty long to explain.

Related

Use multiple key-value filters on an object of objects?

Bit of a lengthy one so those of you who like a challenge (or I'm simply not knowledgeable enough - hopefully it's an easy solution!) read on!
(skip to the actual question part to skip the explanation and what I've tried)
Problem
I have a site that has a dataset that contains an object with multiple objects inside. Each of those objects contains an array, and within that array there are multiple objects. (yes this is painful but its from an API and I need to use this dataset without changing or modifying it.) I am trying to filter the dataset based of the key-value pairs in the final object. However, I have multiple filters being executed at once.
Example of Path before looping which retrieves the key-value pair needed for one hall.
["Hamilton Hall"]["Hire Options"][2].Commercial
After Looping Path of required key-value pair for all halls, not just one (the hall identifier is stored):
[0]["Hire Options"][2].Commercial
Looping allows me to check each hall for a specific key-value pair (kind of like map or forEach, but for an object).
After getting that out of the way back to the question.
How would I go about filtering which of the looped objects are displayed?
What I have Tried
(userInput is defined elsewhere - this happens on a btn click btw)
let results = Object.keys(halls);
for (key of results) {
let weekend = [halls[ `${key}` ][ 'Hire Options' ][4][ 'Weekend function' ]];
if(userInput == weekend) {
outputAll([halls[ `${key}` ]]);
}
}
That filters it fine. However, I run into an issue here. I want to filter by multiple queries, and naturally adding an AND into the if statement doesn't work. I also dont want to have 10 if statements (I have 10+ filters of various data types I need to sort by).
I have recently heard of ternary operators, but do not know enough about them to know if that is the correct thing to do? If so, how? Also had a brief loook at switches, but doesnt seem to look like what I want (correct me if I am wrong.)
Actual Question minus the babble/explanation
Is there a way for me to dynamically modify an if statements conditions? Such as adding or removing conditions of an if statement? Such as if the filter for 'a' is set to off, remove the AND condition for 'a' in the if statement? This would mean that the results would only filter with the active filters.
Any help, comments or 'why haven't you tried this' remark are greatly appreciated!
Thanks!
Just for extra reference, here is the code for retrieving each of the objects from the first object as it loops through them:
(Looping Code)
halls = data[ 'Halls' ];
let results = Object.keys(halls);
for (key of results) {
let arr = [halls[ `${key}` ]];
outputAll(arr);
}
You can use Array.filter on the keys array - you can structure the logic for a match how you like - just make it return true if a match is found and the element needs to be displayed.
let results = Object.keys(halls);
results.filter(key => {
if (userInput == halls[key]['Hire Options'][4]['Weekend function']) {
return true;
}
if (some other condition you want to match) {
return true;
}
return false;
}).forEach(key => outputAll([halls[key]]));

why console.log() creating /**id:4**/ and /**ref:4**/ values?

Few mins ago I did this answer and the answer snippet is below
let obj = {staff_changes: []};
let newStaff=[];
for (let i = 0; i < 4; i++) {
newStaff.push({id: 'staff' +i});
obj.staff_changes.push({
id: i,
newStaff: newStaff
});
}
console.log(obj);
If you run this above snippet, you can see /**id:4**/ and /**ref:4**/ . What is this?
When the code on execution time, that was pushing same duplicate values into a array. So I hope at the starting time it's generating a Id:4 and if the same duplicate value will exist, then just it write a comment like /**ref:4**/ where 4 means Id=:4 which is generated already.
So I want to know Is my understand is correct?. If my understanding is correct , then how can we avoid this? Shall I use object.assign() before push the value into array to avoid this?
Your data structure contains multiple references to the same object. console.log is intelligent enough to abbreviate the output.
Note that (AFAIK), the specification does not guarantee any particular output from console.log for objects that aren't instances of String, so you cannot rely on that output being the same across browsers, versions, phases of the moon, etc.
Consider an infinitely recursive data structure like const a = []; a.push(a); console.log(a), which one would you prefer: your computer to lock up while printing an infinitely recursive array or console.log abbreviating it?
const a = []
a.push(a)
console.log(a)
// [
// /**id:1**/
// /**ref:1**/
// ]
Depending on your console tools, they will display an object like this in different ways. Those comments are telling you there is more information deeper in the object.
If you want to see the internals in a consistent way, you can stringify the whole object
console.log(JSON.stringify(obj));
in which case you get:
{"staff_changes":[{"id":0,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":1,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":2,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]},{"id":3,"newStaff":[{"id":"staff0"},{"id":"staff1"},{"id":"staff2"},{"id":"staff3"}]}]}
In some developer tools, you can expand the object when you log it to the console, but the above string output shows you the whole lot consistently across tools.

Create multiple arrays from one set

I have an array of items, each have an item_type property that could be one of four different types (this is a cart of items). When I attempt to "pay" for the items in the cart, I need to separate all the items remaining into arrays of each item type to send back to the server so the proper tables can be updated. I was just going to use a map to do this but that would require multiple passes (at least the way I was thinking I needed to do it) but is there a quicker way to take one array and split it into multiple arrays based on a property?
Specifically I need to know which array is which (which one has raffle tickets, donations, etc.) so I can post them to the server with the correct property name so the server knows what array is what and how to proceed with each.
Currently in ember this is how I am working through this. Not a lot of code but still I wonder if there is some refactoring that can be done here
// ARRAYS OF CLASSES
itemsArray: Ember.computed.filterBy('model.items','item_type','bid'),
donationsArray: Ember.computed.filterBy('model.items','item_type','donation'),
ticketsArray: Ember.computed.filterBy('model.items','item_type','ticket'),
rafflesArray: Ember.computed.filterBy('model.items','item_type','raffle'),
// ARRAYS OF JUST CLASS IDS
itemIds: Ember.computed.mapBy('itemsArray','id'),
donationIds: Ember.computed.mapBy('donationsArray','id'),
ticketIds: Ember.computed.mapBy('ticketsArray','id'),
raffleIds: Ember.computed.mapBy('rafflesArray','id'),
Ok, so first of all your code is absolutely fine.
I was interested in comparing performance of using observer instead of creating 4 computed properties, so I created this twiddle. I haven't found good way to be absolutely sure 1 way is absolutely faster than another, but you here's another way you could use in your code.
createArrays: Ember.on('init', Ember.observer('model.items.#each.item_type', function() {
console.log('observer fired');
const items = this.get('model.items'),
itemsArray = [],
donationsArray = [],
rafflesArray = [],
ticketsArray = [];
items.forEach(item => {
switch (Ember.get(item, 'item_type')) {
case 'bid':
itemsArray.pushObject(item);
break;
case 'donation':
donationsArray.pushObject(item);
break;
case 'ticket':
ticketsArray.pushObject(item);
break;
default:
rafflesArray.pushObject(item);
break;
}
});
this.setProperties({
itemsArray,
donationsArray,
ticketsArray,
rafflesArray
});
})),
But I don't think it's worth changing to.
Another thing you could do is programatically create those 8 computed properties, but it would be more code than it is now probably. If you would have 10 instead of 4 item_type's it would be worth to do.

Is there a way to map a value in an object to the index of an array in javascript?

Prepending that a solution only needs to work in the latest versions of Chrome, Firefox, and Safari as a bonus.
-
I am trying to use an associative array for a large data set with knockout. My first try made it a true associative array:
[1: {Object}, 3: {Object},...,n:{Object}]
but knockout was not happy with looping over that. So I tried a cheating way, hoping that:
[undefined, {Object}, undefined, {Object},...,{Object}]
where the location in the array is the PK ID from the database table. This array is about 3.2k items large, and would be iterated over around every 10 seconds, hence the need for speed. I tried doing this with a splice, e.g.
$.each(data, function (index, item) {
self.myArray.splice(item.PKID, 0, new Object(item));
}
but splice does not create indices, so since my first PKID is 1, it is still inserted at myArray[0] regardless. If my first PK was 500, it would start at 0 still.
My second thought is to initialize the array with var myArray = new Array(maxSize) but that seems heavy handed. I would love to be able to use some sort of map function to do this, but I'm not really sure how to make the key value translate into an index value in javascript.
My third thought was to keep two arrays, one for easy look up and the other to store the actual values. So it combines the first two solutions, almost, by finding the index of the object in the first example and doing a lookup with that in the second example. This seems to be how many people manage associative arrays in knockout, but with the array size and the fact that it's a live updating app with a growing data set seems memory intensive and not easily manageable when new information is added.
Also, maybe I'm hitting the mark wrong here? We're putting these into the DOM via knockout and managing with a library called isotope, and as I mentioned it updates about every 10 seconds. That's why I need the fast look up but knockout doesn't want to play with my hash table attempts.
--
clarity edits:
so on initial load the whole array is loaded up (which is where the new Array(maxLength) would go, then every 10 seconds anything that has changed is loaded back. That is the information I'm trying to quickly update.
--
knockout code:
<!-- ko foreach: {data: myArray(), afterRender: setInitialTileColor } -->
<div class="tile" data-bind="attr: {id: 'tileID' + $data.PKID()}">
<div class="content">
</div>
</div>
<!-- /ko -->
Then on updates the hope is:
$.each(data.Updated, function (index, item) {
var obj = myModel.myArray()[item.PKID];
//do updates here - need to check what kind of change, how long it's been since a change, etc
}
Here is a solution how to populate array items with correct indexes, so it doesn't start from the first one (0 (zero) I meant)
just use in loop
arr[obj.PKID] = obj;
and if your framework is smart (to use forEach but not for) it will start from your index (like 500 in case below)
http://jsfiddle.net/0axo9Lgp/
var data = [], new_data = [];
// Generate sample array of objects with index field
for (var i = 500; i < 3700; i++) {
data.push({
PKID: i,
value: '1'
});
}
data.forEach(function(item) {
new_data[item.PKID] = item;
});
console.log(new_data);
console.log(new_data.length); // 3700 but real length is 3200 other items are undefined
It's not an easy problem to solve. I'm assuming you've tried (or can't try) the obvious stuff like reducing the number of items per page and possibly using a different framework like React or Mithril.
There are a couple of basic optimizations I can suggest.
Don't use the framework's each. It's either slower than or same as the native Array method forEach, either way it's slower than a basic for loop.
Don't loop over the array over and over again looking for every item whose data has been updated. When you send your response of data updates, send along an array of the PKIds of the updated item. Then, do a single loop:
.
var indexes = []
var updated = JSON.parse(response).updated; // example array of updated pkids.
for(var i=0;i<allElements.length;i++){
if(updated.indexOf(allElements[i].pkid)>-1)
indexes.push(i);
}
So, basically the above assumes you have a simple array of objects, where each object has a property called pkid that stores its ID. When you get a response, you loop over this array once, storing the indexes of all items that match a pk-id in the array of updated pk-ids.
Then you only have to loop over the indexes array and use its elements as indexes on the allElements array to apply the direct updates.
If your indexes are integers in a reasonable range, you can just use an array. It does not have to be completely populated, you can use the if binding to filter out unused entries.
Applying updates is just a matter of indexing the array.
http://jsfiddle.net/0axo9Lgp/2/
You may want to consider using the publish-subscribe pattern. Have each item subscribe to its unique ID. When an item needs updating it will get the event and update itself. This library may be helpful for this. It doesn't depend upon browser events, just arrays so it should be fairly fast.

Problem with ordering headings in a sorted list

I am building a web application in which I build a sorted list out of an object like this:
{head: {subhead: [list_items], subhead: [list_items]}, head: {subhead: [list_items]}}.
My problem is that I have to ensure the headings and subheading always follow in a certain order. This is complicated by the fact that headings and subheadings that may be added later on also need to follow this order. So the only way I could think to inform the parser of the order would be to give it some data like this:
{heads: [head1, head2, head3], subheads: {head1: [subhead1_1, subhead1_2], head2: [subhead2_1, subhead2_2, subhead2_3]}},
but that strikes me as overly verbose and repeating data that would be in the original data structure.
You might as well use an array (or your own structure) for this since you want it to be ordered. Your own structure might look like:
function Head(name) {
this.name = name;
this.arr = [];
}
So instead of the structure:
var thing = {
food: {fruit: [apples, oranges], vegetables: [carrots, peas]},
animals: {domestic: [cat, dog], wild: [lion, bear]}
}
You might do:
var thing = [new Head('food'), new Head('animals')]
Then:
thing[0].arr.push('apples');
thing[0].arr.push('oranges');
etc.
Of course you don't have to make a class, since you can actually attach properties to arrays in javascript. But making the datastructure would be a bit more of a pain.

Categories