I need help in keeping track of objects in an array. I have tried giving each object an arrayIndex var, so I can splice by getting that var which represents the index in the Array.
object0.arrayIndex = 0;
object1.arrayIndex = 1;
object2.arrayIndex = 2;
object3.arrayIndex = 3;
But this is problematic if you move objects to different arrays. Objects would move from different places and therefore the arrayIndex var needs to be constantly updated.
I have done this by adding an static ID to each object. With a for loop I check each object for the corresponding object ID I want to splice
var objectID:Number = objectArrayTarget.id;
for (var t:int; t<_objectArrayLayer1.length; t++)
{
if (objectID == _objectArrayLayer1[i].id)
{
var indexOfObject:Number = (_objectArrayLayer1.indexOf(_objectArrayLayer1[i]));
}
}
_objectArrayLayer1.splice(indexOfObject, 1);
While this works is there a more efficient way of keeping track of objects in an Array? With 100+ objects this might create some slowdown
P.S. These objects are getting spliced and then pushed to a new array.
If your "Objects would move from different places" means same object from one place to another place, there is no arrayIndex needed
var indexOfObject:Number = _objectArrayLayer1.indexOf(targetObj);
if (indexOfObject >= 0) {
_objectArrayLayer1.splice(indexOfObject, 1);
}
If it means different object, like a copy, you could compare some properties to get the targetObj
for (var t:int = 0; t<_objectArrayLayer1.length; t++)
{
if (targetObj.id == _objectArrayLayer1[i].id)//assume id is unique key of the object
{
break;//i is the index here
}
}
if (i != _objectArrayLayer1.length) {//find target object
}
If the object type has a unique key, like a id, or you can make a unique key with some properties of the object, like name + "_" + order, you could use dictionary, Like Patel mentioned.
var dic:Dictionary = new Dictionary(true);
dic[obj1.id] = obj1;
dic[obj2.id] = obj2;//assume id the unique key,or you can use other key
So you can find obj like this
var obj:Object = dic[target.id]
Instead of using an Array.
I think you should use a Set implementation like Hashset
You'll get constant-time lookup, no sorting required,you can add,remove and lookup for object.
Related
The problem with the ECMA standard for sort of Object.keys() is known:
Object.keys() handle all keys with integer (example: 168), including integer as strings (example: "168"), as a integer. The result is, both are the same (168 === "168"), and overwrite itself.
var object = {};
object["168"] = 'x';
object[168] = 'y';
Object.keys(object); // Array [ "168" ]
object[Object.keys(object)]; // "y"
Interestingly, all keys (including pure integer keys) are returned as a string.
The ecma262 wrote about this: All keys will be handle as a integer, expect the key is a String but is not an array index.
https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
That should tell us: 168 === "168". A toString() do not solve the problem.
var object = {};
object[[3].toString()] = 'z';
object[[1].toString()] = 'x';
object[[2].toString()] = 'y';
Object.keys(object);
// Array(3) [ "1", "2", "3" ]
Paradoxically, in this case, only integer apply as "enumerable" (it's ignoring array.sort(), that sort also strings with letters.).
My question about this is simple: How can i prevent the sort function in Object.keys()? I have testet the Object.defineProperties(object, 1, {value: "a", enumerable: true/false}), but that mean not realy enumerable in the case of integer or string or integer-like string. It means only should it be counted with or not. It means "counted" like omit (if it false), not "enumerabled" like ascending or descending.
A answere like that is not a good answer: Please use only letters [a-zA-Z] or leastwise a letter at the first position of keyword.
What I want: That the keys are not sorted, but output in the order in which they were entered, whether integer, string or symbol.
Disclaimer: Please solutions only in JavaScript.
Javascript Objects are unordered by their nature. If you need an ordered object-like variable I would suggest using a map.
To achieve what you're looking for with a map instead of object you'd do something like the below:
var map1 = new Map();
map1.set("123", "c");
map1.set(123, "b");
var iterator1 = map1.keys();
var myarray = [];
for (var i = 0; i < map1.size; i++) {
myarray.push(iterator1.next().value);
}
console.log(myarray);
// Array ["123", 123]
Unfortunately it's not compatible with IE and I'm not sure how else you could achieve what you need without it. A quick Google did return something about jQuery maps, though.
If you don't want to use jQuery and still need to support IE some points are below:
Is there anything stopping you using an array rather than JS object to store the data you need? This will retain the order per your requirements unlike objects. You could have an object entry in each iteration which represents the key then use a traditional foreach to obtain them as an array. I.e.
The array:
var test_array = [
{key: 123, value: 'a value here'},
{key: "123", value: 'another value here'}
];
// console.log(test_array);
Getting the keys:
var test_array_keys = [];
test_array.forEach(function(obj) { test_array_keys.push(obj['key']); } );
// console.log(test_array_keys);
Then if you needed to check whether the key exists before adding a new entry (to prevent duplicates) you could do:
function key_exists(key, array)
{
return array.indexOf(key) !== -1;
}
if(key_exists('12345', test_array_keys))
{
// won't get here, this is just for example
console.log('Key 12345 exists in array');
}
else if(key_exists('123', test_array_keys))
{
console.log('Key 123 exists in array');
}
Would that work? If not then the only other suggestion would be keeping a separate array alongside the object which tracks the keys and is updated when an entry is added or removed to/from the object.
Object Keys sorted and store in array
First Creating student Object. then sort by key in object,last keys to store in array
const student={tamil:100, english:55, sci:85,soc:57}
const sortobj =Object.fromEntries(Object.entries(student).sort())
console.log(Object.keys(sortobj))
use map instead of an object.
let map = new Map()
map.set("a", 5)
map.set("d", 6)
map.set("b", 12)
to sort the keys (for example, to update a chart data)
let newMap = new Map([...map.entries()].sort())
let keys = Array.from(newMap.keys()) // ['a','b','d']
let values = Array.from(newMap.values()) // [5,12,6]
I have a dictionary of prices and quantities. I am getting updates on the price and values multiple times in a second so I don't want to store them in an array because dictionary are much faster.
let mainPriceValDict = [Double:Double]()
The data is coming in as an array of JSON so I am using codable to parse the JSON and put it into a dictionary. When I use the data, it needs to be sorted in ascending and/or descending order because I am looping through each price in order to get to a certain total quantity. The format that the array is in that I am looping through is as follows:
let loopingArray = [PriceQuantityEntry]()
struct PriceQuantityEntry {
let price : Double
let size : Double
}
I want to sort the prices which are the keys in the dictionary above and convert them into an array of PriceQuantityEntry. What is the best way to do this? In ascending and deciding order. I have tried first getting all the keys sorted and then grabbing associated values and putting them into the array in order but this seems like more processing than this task actually requires.
I think the best way to do this would be to put a custom initializer in the struct to convert the dictionary value to a value of type PriceQuantityEntry but I am not exactly sure how that would work with the sorting.
This is what I am currently doing to get it to work. I just feel like there is a more efficient way for it to be done. If you feel like I should keep the structure as an array instead of converting it to a dict, let me know.
loopingArray = self.mainPriceValDict.sorted { $0.0 < $1.0 }.map { PriceQuantityEntry(price: $0.0, size: $0.1) }
If you are getting a lot of updates to individual entries, both a dictionary and an array may cause memory copies of the whole memory structure every time an entry is changed.
I would suggest using objects (classes) instead of structures. This will allow you to use both an array and a dictionary to reference the object instances. The dictionary will provide direct access for updates and the array will allow sequential processing in forward or backward order.
[EDIT] Example:
class PriceQuantityEntry
{
static var all:[PriceQuantityEntry] = []
static var prices:[Double:PriceQuantityEntry] = [:]
var price : Double
var size : Double
init(price:Double, size:Double)
{
self.price = price
self.size = size
PriceQuantityEntry.all.append(self)
// PriceQuantityEntry.all.resort() // on demand or when new prices added
PriceQuantityEntry.prices[price] = self
}
class func update(price:Double, with size:Double)
{
if let instance = PriceQuantityEntry.prices[price]
{ instance.size = size }
else
{
let _ = PriceQuantityEntry(price:price, size:size)
PriceQuantityEntry.resort()
}
}
class func resort()
{
PriceQuantityEntry.all.sort{$0.price < $1.price}
}
}
// if adding multiple initial entries before updates ...
let _ = PriceQuantityEntry(price:1, size:3)
let _ = PriceQuantityEntry(price:1.25, size:2)
let _ = PriceQuantityEntry(price:0.95, size:1)
PriceQuantityEntry.resort()
// for updates ...
PriceQuantityEntry.update(price:1, with: 2)
// going throug list ...
var count:Double = 0
var total:Double = 0
var quantity:Double = 5
for entry in PriceQuantityEntry.all
{
total += min(entry.size,quantity-count) * entry.price
count = min(quantity,count + entry.size)
if count == quantity {break}
}
Does is any utility for add the index of each object in array as a property in knockout js?
I'm doing that in two steps, I want to add the index property for each object at the same time that is pushed to the array. Here is my code.
ko.utils.arrayPushAll(array,
ko.mapping.fromJS(result)());
for (var i = 0; i < array.length; i++) {
array[i].index = i;
}
At the risk of not answering your question:
If you loop through an array in a foreach: binding, you will have an observable $index available for you to reference the index in the array.
If you made your array an observableArray, you could attach a computedObservable to your model that performs soemthing like:
myArray()[i].index = ko.pureComputed(()=>myArray().indexOf(myArray()[i]))
I believe that index will be a observable that will notify subscribers when the array is changed (and hence the index needs to be recalculated).
I have an array of objects with different properties and I need to filter the array in a way that a specific property is not duplicated.
For example:
var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
Now suppose I want to filter the objects on the property baz. What is the most efficient way of filtering the array, so that no two elements have the same value for baz after the operation?
In my example, the result should only contain:
var result:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"}]
since the other objects would have duplicate entries for the baz property.
The order of the result array is not important, neither is which object of those with identical values for baz makes it into the result array.
Update:
The object array is used as a dataprovider to populate a s:Datagrid with information about chatrooms. The objects in the array carry related information (like the room's ID on the server and some other config settings).
The baz property I used in my example is actually the ID of the language the chat room is configured to use and I want to create a s:DropDownList with which I can filter the Datagrid for individual languages (e.g. show all rooms that use "German").
It is very likely to have many objects with the same language ID, but I only want each language Id to show up once in the DropDownList.
I need to extract that information from the Datagrids's dataprovider (the source array) and cannot retrieve my languages directly since the DropDownList is part of a generic DatagridHeaderRenderer that is used in many different Datagrids with different data.
private var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
private var filteredArray:Array;
private var keys:Object = {};
private function filterArray():void{
filteredArray = arr.filter(removeDupes);
}
private function removeDupes(item:Object, idx:uint, arr:Array):Boolean {
if (keys.hasOwnProperty(item.baz)){
return false;
} else {
keys[item.baz] = item;
return true;
}
}
private function resetFilter():void{
filteredArray = new Array();
keys = {};
}
Modified from multiple sources but primarily: http://blog.flexexamples.com/2007/08/05/removing-duplicate-items-from-an-array-using-the-arrayfilter-method/
Or you could just use an arrayCollection and its built-in filterFunction. See: http://cookbooks.adobe.com/post_Using_the_to_ArrayCollection_s_filterFunction-5441.html
On the surface of it looks like it should work. Using Array.filter is usually about twice the time of doing the same thing in a loop.
I'd argue that' Dom's removeDupes function doesn't do exactly what's required, although it might be a more generic approach (if, for example, the === isn't a good comparison function, then this gives you a way of extending it.) But using hasOwnPropery is a big no-no. You should never touch it - that function only exists for ES compatibility. It is evil otherwise - both a potential security hole (as it is defined on Object.prototype and thus is easy to override for the foreign code) and is slow (for the same reason - the lookup of the functions defined on prototype is slower then those defined in a class).
public function Test()
{
super();
var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
this.removeDuplicates(array, "baz").map(this.objectTracer);
// { foo : a3, baz : c3, bar : b1 }
// { foo : a1, baz : c2, bar : b4 }
// { foo : a0, baz : c1, bar : b3 }
}
private function objectTracer(object:Object, index:int, all:Array):void
{
var result:String = "";
for (var p:String in object)
result += ", " + p + " : " + object[p];
if (result) result = result.substr(2);
trace("{ " + result + " }");
}
private function removeDuplicates(array:Array, on:String):Array
{
var result:Array = array.concat();
// note that since we use `Dictionary' the
// the comparison between objects is the same as `==='
// if all values are strings, you can use `Object' to
// save some space.
var hash:Dictionary = new Dictionary();
var temp:Object;
for (var i:int, j:int = result.length - 1; j >= i; j--)
{
temp = result[j][on];
if (temp in hash)
{
result[j] = result[i];
j++; i++;
}
else hash[temp] = true;
}
// note that we could `shift()` until we get to `i'
// but when we do it, we actually reallocate the array every time
// so `slice()' must, in theory, be more efficient
return result.slice(i);
}
I have an ActionScript 3 array that lists pairs of items like this:
pairs[0] = Array('ItemA', 'ItemB');
pairs[1] = Array('ItemA', 'ItemC');
pairs[2] = Array('ItemC', 'ItemD');
pairs[3] = Array('ItemC', 'ItemE');
pairs[4] = Array('ItemF', 'ItemG');
pairs[5] = Array('ItemF', 'ItemH');
And I need to loop over the array in some way to find all overlapping pairs (any pairs that share common pairs).
For example, ItemA is paired with ItemB and ItemC, so they belong in a group together. ItemC is also paired with ItemD and ItemE so they also need to be a part of the first group.
ItemF, ItemG and ItemH do not overlap with any of the items fromt he first group, so they need to be put into their own group.
The resulting array would need to be something like:
groups[0] = Array('ItemA', 'ItemB', 'ItemC', 'ItemD', 'ItemE');
groups[1] = Array('ItemF', 'ItemG', 'ItemH');
Thanks for any help and suggestions!
Edit:
A little bit of a back story; I'm trying to group together movie clips that overlap each other in 2D to create groups or clusters (might be a better word).
So if I have 3 movieclips on the stage and ClipA overlaps with ClipB and ClipB overlaps ClipC (but ClipA doesn't directly overlap ClipC) they should all be grouped together as they are all a part of the same cluster. This way should a new clip overlap any single item in a cluster, it will be added to that group's array.
I've already got the code worked out to find overlapping elements which is producing this pairs list, now I need to condense it into tidy groups.
An algorithm like the example below should work.
NOTE: This is not the most efficient or concise way to write this code (it's certainly more repetitive than it needs to be), but I wanted to keep it clear and simple for this example. [Also, I haven't tested this code--it's presented as pseudo-code only--so if you find an error, please just let me know, and I'll fix it]
var idx:Object = new Object;
var groups:Array = new Array();
for( var i:int = 0; i<pairs.length; ++i ) {
var onePair:Array = pairs[i];
// which groups do the two items belong to?
var g1:Array = idx[onePair[0]];
var g2:Array = idx[onePair[1]];
if( !g1 ) {
// if item #1 is not yet in a group, then add it to item #2's
// existing group, or if neither group exists yet, just create a new one
g1 = g2;
if( !g1 ) {
g1 = [];
groups.push(g1);
}
g1.push( onePair[0] );
// ensure that the idx properly reflects the location of the new item
idx[onePair[0]] = g1;
}
// now do the same for the second item... but g1 will never be null, so
// this case is a little simpler.
if( !g2 ) {
g2 = g1;
g2.push( onePair[1] );
idx[onePair[1]] = g2;
}
if( g1 != g2 ) {
// now, if they're not already the same group, then merge the two
// groups, and update the idx to reflect the merge.
for( var z:int=0; z<g2.length; ++z ) {
idx[g2[z]] = g1;
g1.push( g2[z] );
g2.splice(0);
}
}
}
groups will end up being an array of arrays, just like you asked for -- but there will be a few empty arrays that can be discarded. Just prune (or ignore) the empty ones, and you'll have your groups.
the basic idea here, is that idx provides a lookup table that indicates, throughout the indexing process, for any given item, which group it's in (if any). This allows us to determine whether an item has been encountered previously or not, and if so, to utilize it's existing group.
You can use an Object to keep the track of the association of a pair iten and a group, the key will be each item of your pair.
Here a litle snippet that make the works :
var pairs:Array=[];
pairs[0] = ['ItemA', 'ItemB'];
pairs[1] = ['ItemA', 'ItemC'];
pairs[2] = ['ItemC', 'ItemD'];
pairs[3] = ['ItemC', 'ItemE'];
pairs[4] = ['ItemF', 'ItemG'];
pairs[5] = ['ItemF', 'ItemH'];
// will contain group created
var groups:Array=[];
// will contain association between a pair item and a group
var pair2group:Object={};
// function that turn pairs into groups
function makeGroups(pairs:Array):void{
var pairLen:int = pairs.length;
for (var i:int=0;i<pairLen;i++){
var pair:Array = pairs[i];
var item1:String = pair[0];
var item2:String = pair[1];
var group:Array = pair2group[item1];
// is first pair item already in a group
if (group == null) {
// no so create a new group
group=[];
// create the association
pair2group[item1] = group;
// add the item to the group we have created
group.push(item1);
// add it to all the groups
groups.push(group);
}
// is the second pair item into a grouo
if (pair2group[item2] == null) {
// no so add it to the group where the first item belong
group.push(item2);
// create the association for the second item
pair2group[item2] = group;
}
}
}
// ---- test
makeGroups(pairs);
trace(groups.length);
trace(groups[0]);
trace(groups[1]);
After lots of playing around here's the solution I came up with.
This will take an 2D overlapArray that has pairs and produce a group list with unique values.
I used a in_array() function to duplicate PHP's handy function for finding if an item is already in an array.
for each(var pair:Array in overlapArray) {
var pairInGroup = false;
for each(var group:Array in overlapArrayGroups) {
if(in_array(pair[0],group) || in_array(pair[1],group)) {
if(!in_array(pair[0],group)) {
group.push(pair[0]);
}
if(!in_array(pair[1],group)) {
group.push(pair[1]);
}
pairInGroup = true;
}
}
if(!pairInGroup) {
overlapArrayGroups.push(pair);
}
}
The in_array() function:
public static function in_array( needle:String, haystack:Array ):Boolean {
for( var a = 0; a < haystack.length; a++ ) {
if( haystack[a] == needle ) {
return true;
} else if( haystack[a] is Array ) {
return in_array(needle, haystack[a]);
}
}
return false;
}