Removing named properties from "decorated" array - javascript

Although my question stems from DataTables.net, I imagine it is applicable elsewhere:
I retrieve an array-like object from a DataTables-created table like this:
var data = tableInstance.data(); // tableInstance is already a DataTables table instance
But the data, while array-like, is actually an object decorated with the DataTables API, resulting in an "array" that looks something like this (reduced to a fake "brief" version):
[
0: {thing: "stuff"},
1: {thing: "nextStuff"},
$: function(){},
button: function() {},
length: 2
]
I would like to isolate just the actual array. Does anybody spot an elegant way of doing this? The "obvious" way is to just iterate X times, up to data.length. For example, using an "each" iterator, which inherently does just that:
var newData = [];
data.each(function (el, index) {
newData.push(el);
})
But I can't help wondering if there's a better way. Generating the new array (or editing in-place... no requirement for it to be new) by removing unwanted properties, rather than by pushing wanted items into a new array.
Or is this just too much of a micro-optimization (even with tens of thousands of items) to even bother with?

There is a better way. Use Array.from.
const newData = Array.from(data)

Related

Prevent pushing to array if duplicate values are present

I'm mapping an array and based on data i'm pushing Option elements into an array as follows
let make_children: any | null | undefined = [];
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { make: {} | null | undefined; }, key: any) => {
make_children.push(
<Option key={key}>{item.make}</Option>
);
});
Following data array has several objects and these objects have an attribute called model.
buyerActivityResult.simulcastMyAccount.data
I want to prevent pusing Options to my array if the attribute model has duplicate data. It only has to push once for all similar model values.
How can i do it?
I tried something like this
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { model: {} | null | undefined; }, key: any) => {
model_children.indexOf(item.model) === -1 && model_children.push(
<Option key={key}>{item.model}</Option>
);
});
But still duplicate values are being pushed into my array.
Its difficult to tell what you are trying to achieve but it looks like a map may not be the right tool for the job.
A map returns the same sized length array as that of the original array that you are calling map on.
If my assumptions are correct, your buyerActivityResult.simulcastMyAccount.data array has duplicate values, and you want to remove these duplicates based on the model property? One way to achieve this would be to use the lodash library for this, using the uniq function:
const uniqueResults = _.uniq(buyerActivityResult.simulcastMyAccount.data, (item) => item.model);
The Array.prototype.map() method is supposed to be used for manipulating the data contained into the array performing the operation. To manipulate data from other variables I recommend to use a for-loop block.
If item.model is an object, the function Array.prototype.indexOf() always returns -1 because it compares the memory address of the objects and does not do a deep comparison of all properties values.
The usual solution to remove duplicate data from an array is converting the Array into a Set then back to an Array. Unfortunately, this works only on primary type values (string, number, boolean, etc...) and not on objects.
Starting here, I will review your source code and do some changes and explain why I would apply those changes. First of all, assuming the make_children array does not receive new attribution later in your code, I would turn it into a constant. Because of the initialization, I think the declaration is overtyped.
const make_children: any[] = [];
Then I think you try to do too much things at the same time. It makes reading of the source code difficult for your colleagues, for you too (maybe not today but what about in few weeks...) and it make testing, debugging and improvements nearly impossible. Let's break it down in at least 2 steps. First one is transforming the data. For example remove duplicate. And the second one create the Option element base on the result of the previous operation.
const data: { make: any }[] = buyerActivityResult?.simulcastMyAccount?.data || [];
let options = data.map((item) => !!item.model); // removing items without model.
// Here the hard part, removing duplicates.
// - if the models inside your items have a property with unique value (like an ID) you can implement a function to do so yourself. Take a look at: https://stackoverflow.com/questions/2218999/remove-duplicates-from-an-array-of-objects-in-javascript
// - or you can use Lodash library like suggested Rezaa91 in its answer
options = _.uniq(data, (item) => item.model);
Now you only have to create the Option elements.
for (var i = 0; i < options.length; i++) {
model_children.push(<Option key={i}>{options[i].model}</Option>);
}
// OR using the Array.prototype.map method (in this case, do not declare `model_children` at the beginning)
const model_children:[] = options.map((opt:any, i:number) => <Option key={i}>{opt.model}</Option>);
Despite the lack of context of the execution of the code you provided I hope my answer will help you to find a solution and encourage you to write clearer source code (for the sake of your colleagues and your future self).
PS: I do not know anything about ReactJs. forgive me my syntax mistakes.

jQuery Get associative array Key from index

My question is the following, in an array:
var array = new Array();
array['abc'] = 'value1';
array['def'] = 'value2';
How do I get the associative key of an array if I have its index number? Let's say I want associative key of arr[0]'s associative key ( => 'abc'), or associative key of arr[1] '=> 'def'). How is this possible in jQuery?
Let's be clear, I am not looking for the value and I do not need to use $.each(). I just need to link 0 to 'abc' and 1 => 'def' etc... Unfortunately something like arr[0].assoc_key() doesn't seem to exist T_T
Thanks a bunch.
All right so the solution is pretty simple, you need to create an object which associates indeces with keys as well as keys with values. Here is a JSBin that works. Please note that to add an element, you need a custom function (addElement in this case) to be able to have both indeces and keys associated at the right places. This is a rough draft to give you an idea of how it can be done!
JSBin
If you have any question or if that wasn't exactly what you expected, simply edit your question and I'll have another glance at it. It HAS to be a custom made object if you want the behavior you asked for.
Javascript doesn't have a native Dictionary type, you would have to write it. – T McKeown
It isn't possible and jQuery doesn't come into the picture at all. If you use an array as a dictionary like that, you are doing something wrong. – Jon
rethink the way you are doing it. Maybe try array[0] = {key: 'abc', value: 'value1'} – Geezer68
#Geezer68, objects do not support multidimentional data, the array I'm working on is 3 levels deep (I know I didn't says so in my original post, but I didn't think it was relevant).
Anyway, thank you guys, it answers the question! I will rethink it then ;-)
EDIT: I guess I'll just add a level:
var array = new Array();
array[] = 'abc';
array[0] = 'value1'
I don't know an other than using a for ... in. So here how i do it and hope you get a better answer (because i want to know aswell!).
var array = new Array();
array['abc'] = 'value1';
array['def'] = 'value2';
var listKeys = [];
for(x in array) listKeys.push(x);
console.log(listKeys); // ['abc', 'def']
but using [string] on an array object is adding property to the object, not the array. So it may be better to initialise it like that :
var array = {};
You might learn more information on this technique in this question and some restriction on why you should not rely on that.

How do I sort a JSON object by a nested value?

I have an ajax call that returns a JSON object that is pretty complex and I'm having a hard time sorting it.
My call:
$.post('/reports-ajax',arguments, function(data) {}
The response:
{
"10001":{
"unitname":"Fort Worth",
"discounts":{"12-02-2012":"34.810000","12-03-2012":"20.810000","12-04-2012":"27.040000"},
"gross":{"12-02-2012":"56.730000","12-03-2012":"19.350000","12-04-2012":"66.390000"},
"net":{"12-02-2012":"61.920000","12-03-2012":"98.540000","12-04-2012":"39.350000"},
"discounts_total":82.66,
"gross_total":82.47,
"net_total":99.81,
"number":10001
},
"10002":{
"unitname":"Dallast",
"discounts":{"12-02-2012":"12.600000","12-03-2012":"25.780000","12-04-2012":"47.780000","12-05-2012":"45.210000"},
"gross":{"12-02-2012":"29.370000","12-03-2012":"91.110000","12-04-2012":"60.890000","12-05-2012":"51.870000"},
"net":{"12-02-2012":"16.770000","12-03-2012":"65.330000","12-04-2012":"13.110000","12-05-2012":"06.660000"},
"discounts_total":131.37,
"gross_total":33.24,
"net_total":101.87,
"number":10002
},
"32402":{
"unitname":"Austin",
"discounts":{"12-05-2012":"52.890000","12-02-2012":"22.430000","12-03-2012":"58.420000","12-04-2012":"53.130000"},
"gross":{"12-05-2012":"25.020000","12-02-2012":"2836.010000","12-03-2012":"54.740000","12-04-2012":"45.330000"},
"net":{"12-04-2012":"92.200000","12-05-2012":"72.130000","12-02-2012":"13.580000","12-03-2012":"96.320000"},
"discounts_total":186.87,
"gross_total":161.1,
"net_total":174.23,
"number":32402
}
}
I go over the function with a standard each call and do some awesome stuff with highcharts but now I'm trying to sort the responses by the net_total call and I can't figure it out.
I tried .sort() and it errors out that its not a function. I've been reading for a while but guess I'm not finding the right results. This looked promising: Sorting an array of JavaScript objects but it failed with the .sort is not a function. It seems most .sort are on [] arrays not full objects..
Any help would be greatly appreciated.
Sorting objects doesn't make sense since object keys have no positional value. For example, this:
{ a:1, b:2 }
and this:
{ b:2, a:1 }
are exactly the same object. They're not just similar, they're the same.
Nothing in javascript per se gives object keys any positional value. Some people perhaps are mistaken in the belief that:
for (var key in obj) {
iterates through the object keys in a specific sequence. But this is wrong. You should always assume that the for .. in loop processes object keys in random order, always, all the time.
Obviously, if you're going to write a web browser, you're not going to implement a random number generator to parse a for .. in loop. Therefore most web browsers have an accidental stability to how the for .. in loop processes object keys.
Developers who learn javascript by playing around with the browser may figure out that their browser iterates through objects in alphabetical order for example, or the order the keys were added to the object. But this is totally accidental and cannot be relied upon. The browser vendor may change this behavior in the future without violating any backwards compatability (except with buggy scripts written by people who believe objects have a sort order). Not to mention that different browsers have different implementations of javascript and therefore not necessarily have the same internal key ordering of objects.
All the above is besides the point. "Key sort order" does not make any sense in javascript and any behavior observed is merely implementation detail. In short, javascript object does not have key order, just assume it's random.
Solution
Now, what you're really trying to do is not sort the object (you can't, it doesn't make sense). What you're really trying to do is process the object attributes in a specific order. The solution is to simply create an array (which has sorting order) of object keys and then process the object using that array:
// First create the array of keys/net_total so that we can sort it:
var sort_array = [];
for (var key in Response) {
sort_array.push({key:key,net_total:Response[key].net_total});
}
// Now sort it:
sort_array.sort(function(x,y){return x.net_total - y.net_total});
// Now process that object with it:
for (var i=0;i<sort_array.length;i++) {
var item = Response[sort_array[i].key];
// now do stuff with each item
}
What you have there isn't an array and has no order, so you'll have to transform it into an array so you can give it order.
Vaguely:
var array = [];
$.each(data, function(key, value) {
array.push(value);
});
array.sort(function(a, b) {
return a.net_total - b.net_total;
});
Live Example | Source
As GolezTroi points out in the comments, normally the above would lose the key that each entry is stored under in data and so you'd add it back in the first $.each loop above, but in this case the entries already have the key on them (as number), so there's no need.
Or you can replace the first $.each with $.map:
var array = $.map(data, function(entry) {
return entry;
});
// ...and then sort etc.
...whichever you prefer.

Test for value within array of objects

I am dynamically building an array of objects using a process that boils down to something like this:
//Objects Array
var objects = [];
//Object Structure
var object1 = {"id":"foobar_1", "metrics":90};
var object2 = {"id":"some other foobar", "metrics":50};
objects[0] = object1;
objects[1] = object2;
(Let it be said for the record, that if you can think of a better way to dynamically nest data such that I can access it with objects[i].id I am also all ears!)
There's ultimately going to be more logic at play than what's above, but it's just not written yet. Suffice it to say that the "object1" and "object2" parts will actually be in an iterator.
Inside that iterator, I want to check for the presence of an ID before adding another object to the array. If, for example, I already have an object with the ID "foobar_1", instead of pushing a new member to the array, I simply want to increment its "metrics" value.
If I wasn't dealing with an array of objects, I could use inArray to look for "foobar_1" (a jQuery utility). But that won't look into the object's values. The way I see it, I have two options:
Keep a separate simple array of just the IDs. So instead of only relying on the objects array, I simply check inArray (or plain JS equivalent) for a simple "objectIDs" array that is used only for this purpose.
Iterate through my existing data object and compare my "foobar_1" needle to each objects[i].id haystack
I feel that #1 is certainly more efficient, but I can't help wondering if I'm missing a function that would do the job for me. A #3, 4, or 5 option that I've missed! CPU consumption is somewhat important, but I'm also interested in functions that make the code less verbose whether they're more cycle-efficient or not.
I'd suggest switching to an object instead of an array:
var objects = {};
objects["foobar_1"] = {metrics: 90};
objects["some other foobar"] = {metrics: 50};
Then, to add a new object uniquely, you would do this:
function addObject(id, metricsNum) {
if (!(id in objects)) {
objects[id] = {metrics: metricsNum};
}
}
To iterate all the objects, you would do this:
for (var id in objects) {
// process objects[id]
}
This gives you very efficient lookup for whether a given id is already in your list or not. The only thing it doesn't give you that the array gave you before is a specific order of objects because the keys of an object don't have any specific order.
Hmm , i wonder why dont you use dictionary cause that is perfectlly fits your case. so your code will be as below:
//Objects Array
var objects = [];
//Object Structure
var object1 = {"metrics":90};
var object2 = {"metrics":50};
objects["foobar_1"] = object1;
objects["some other foobar"] = object2;
// An example to showing the object existence.
if (!objects["new id"]){
objects["new id"] = {"metrics": 100};
}
else {
objects["new id"].matrics++;
}

Javascript - get the value of a property within a specific JSON array element by its key

I have a JSON structure like this:
{
map: [
{"key1":"valueA1", "key2":"valueA2", "key3":"valueA3"},
{"key1":"valueB1", "key2":"valueB2", "key3":"valueB3"},
{"key1":"valueC1", "key2":"valueC2", "key3":"valueC3"},
.... etc
]
}
... which I load into my javascript app to become an object via JSON.parse().
I want to retrieve (say) the value of key3 from the element of the object array where key2='valueB2'.
I can do it by looping through, but wondered if there was a more elegant (e.g. single line and more efficient) way of doing this, without having to know the index number for the array element?
I've google loads of sites, to little avail. Or would I be better simplifying/removing the array in favour of a simple list of objects?
Thanks.
JSON will usually have double quotations " around all keys and values except for numbers.
However loop is the most efficient and best choice you have. There is new functional array iteration methods, but only available on new JS engines and browsers, they can be found here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
If you have the liberty of changing the JSON structure, you should implement it to be an Object instead of an array of objects where the key is the value of key2 and the value is the Object.
Example:
{
"valueB2": {
"key1": "valueB1",
"key2": "valueB2",
"key3": "valueB3"
}
}
The retrieval of the object would be O(1) and as simple as obj["valueB2"]
There isn't a more elegant way to do this. The only thing you know without looping are the indexes. That's not the identifier you want, so you'll have to inspect the content: loop:
function byKey(arr, key) {
for ( var i=0, L=arr.length; i<L; i++ ) {
if ( arr[i].key1 === key ) {
return arr[i];
}
}
}
Or something like that.
To round off: taking ideas from both answers, I adopted this simpler JSON structure:
[
{"key1":"valueA1", "key2":"valueA2", "key3":"valueA3"},
{"key1":"valueB1", "key2":"valueB2", "key3":"valueB3"},
{"key1":"valueC1", "key2":"valueC2", "key3":"valueC3"},
.... etc
]
with the suggested loop function to find the element for "key2:"valueB2" - the array is accessed by index for most of the time, and only occasionally by the loop-search, so that seemed the best balance. I also realised I could do away with the containing object "map" as it wasn't adding any utility.

Categories

Resources