I'm attempting to write a sort function to use with Array.sort(). I'm a bit stuck on how I can write exactly what I need though.
In my app items are added to this array at different times throughout execution, and each time an item is added, the array is sorted. The items in the Array are all objects and all have a property "weight". If a weight is greater, the item should go first, if it's less the item should go after. That is easy and I have a function that looks like this:
return a.weight - b.weight;
The problem is I have an added requirement that if an item is added later and it has the same weight as another item it MUST be put after that item in the array. It MUST go behind every item in the array that has already been added which has the same weight.
I'm having trouble coming up with a function to make sure that requirement is met every time.
Thanks for the help!
No need for writing a custom sort, the Array's sortOn can handle this case.
You will however need to add a new member to your items, I'll call it 'timestamp'.
arr.sortOn( [ 'weight', 'timestamp' ], [ Array.NUMERIC | Array.DESCENDING, Array.NUMERIC ] );
The first parameter defines which properties will be used for sorting, the second defines the options for each field. See http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#sortOn() for more info.
The |-operator (bitwise OR-operator) is used to pass multiple options for one field.
So, in this case, the first field ('weight') gets sorted numerically and descending.
--EDIT:
For Vectors you need to use a comparison function:
var sortFunc : Function = function (x: <T>, y : <T>):Number{
var dw:Number = y.weight - x.weight
if( dw ==0 ){
//returns negative if y was added later
return x.timestamp - y.timestamp;
}else{
//returns negative if x has a higher weight
return dw;
}
}
vec.sort( sortFunc );
I would suggest to add another property to the object. Something like :
a.index = i; // where, i is the index before sorting
This will allow you to keep track of the order it entered the list before sorting.
Besides this you may also consider keeping another copy of the array itself (with index intact).
Related
I am working on sorting arrays of data within a Collection View. There are 3 ways I would like to sort the data: alphabetically, numerically, and no sort at all (no sort at all should sort the order of the data how it's set up in the model.) The sorting happens via 3 different UIButtons.
In my code below, allAnimalsArray is the full array of data that gets displayed in the Collection View.
// Animal Name button pressed
let sortAZ = allAnimalsArray.sorted(by: { $0.animalName < $1.animalName })
allAnimalsArray = sortAZ
allAnimalsCollectionView.reloadData()
// Animal Weight button pressed
let sortByWeight = allAnimalsArray.sorted(by: { $0.pounds < $1.pounds })
allAnimalsArray = sortByWeight
allAnimalsCollectionView.reloadData()
// NO SORT button pressed
let noSorting = allAnimalsArray
allAnimalsArray = noSorting
allAnimalsCollectionView.reloadData()
My problem is the NO SORT method doesn't change the sort. It keeps the same order as whichever was pressed before it.
It was my understanding .sorted makes a copy of the data so the original data is retained (whereas .sort modifies the original data).
How do I change the NO SORT method above to simply display the data without any sorting? (meaning it's displayed exactly how the data is set up in the model)
you need to
either save a copy of the entire unsorted array and use when you need to revert
or at least save the indices of that array so you can use them to impose the
original unsorted state
If the original array has no sortable order, then I suggest you never modify the original array. Keep a second array that is based on the current sort and use this second array as the basis of the collection view's data model.
When the user chooses a different sort method, you simply update the second array from the original, unmodified array, and then reload the collection view.
let allAnimalsArray = ... // the original array that you never modify
var sortedArray = [YourAnimalClass]()
All of your data source methods are based on sortedArray.
If the user chooses "no sort" then you do:
sortedArray = allAnimalsArray
allAnimalsCollectionView.reloadData()
If the user chooses one of the sorts then you do:
sortedArray.allAnimalsArray.sorted(by: { $0.whatever < $1.whatever })
allAnimalsCollectionView.reloadData()
I am trying to make a CONTAINER class that maintains an array of CRITTER objects (that I have already created and tested. Note, there are various CRITTER subspecies, that are inheriting from the CRITTER super class). The aim is to add and remove CRITTER objects from this array. This is what the CONTAINER class looks like:
class
CONTAINER
create
make
feature
num: detachable INTEGER
list: ARRAY[CRITTER]
make
local
do
create list.make_empty
num := 0
end
addCritter(critter: CRITTER)
do
list.put(animal, num)
num := num + 1
end
removeCritter(critter: CRITTER)
do
list.put (list.at (num), ???) -- put last element in position of element to be removed
list.remove_tail (num) -- remove tail
num := num - 1
end
end
Two issues:
Firstly, I can instantiate the CONTAINER class inside APPLICATION, but when I call
create container.make
container.addCritter(myCritter)
I get a precondition, invalid index violation error on the second line. This may be because I have not set the upper and lower bounds of the array. However, when I try to do so, I get syntax errors. Which is the way to solve this issue?
Secondly, in order to remove an object from the array, it would help if I could get hold of the index value, but I can't see any function that does this, unless I am missing something.
ARRAYs are usually used for fixed-length containers. In your case, with lots of dynamic changes, it's better to use more dynamic structures, for example, ARRAYED_LIST. Similar to ARRAY it provides features to access items by their index, but there are also more convenient ones. New elements can be added by using feature extend. Old elements can be removed by using feature prune if only one element matching a given one needs to be removed, or prune_all, if all matching elements need to be removed. The word "matching" denotes either reference or object equality, depending on which comparison criteria is required: = or ~. The comparison criteria is changed using feature compare_objects.
Some general observations:
There is no need to track number of elements yourself, usually there is a feature count that provides this number.
Indexes in Eiffel usually start with 1, not 0.
The declaration detachable INTEGER is equivalent to INTEGER because INTEGER is expanded and all expanded types are attached regardless of any attachment marks.
The following discussion might also be useful:
How to initialise an array of objects in Eiffel?
I have an Ember.Array that I need to rearrange by moving one element to another. Right now, I have the function:
moveElement: (from, to) ->
from_value = array.objectAt(from)
array.removeAt(from)
array.insertAt(to, from_value)
The problem is, I use this array in a computed property and that is firing in between the removeAt and insertAt. How can I make sure these changes are atomic?
Thanks!
So, I managed to get it working by using arrayContentWillChange, arrayContentDidChange, and array primitives:
moveElement: (from, to) ->
array = #get('array')
# make all changes to array atomically
array.arrayContentWillChange(from, 1, 0)
from_value = array[from]
array.splice(from,1)
array.splice(to,0,from_value)
array.arrayContentDidChange(to, 0, 1)
I'm not sure if I'm using the arrayContent..Change functions correctly. They take 3 arguments: startIdx, removeAmt, addAmt. Which represent the start index, number of elements to remove, and number of elements to add. I used my from and to values to show that I am removing one element at from and adding one element at to.
Seems to be working now.
Ok so I'll start off with what I am trying to do here with an Array. So I have a grid of 1-59, Now the user gets to pick 5 numbers. When the user picks a number it goes into a box in the top corner of the screen. So what I am trying to make an Array for is a user selects a number it goes into the first box then the second number goes into the second one and so on.
I am still new to AS3 and I have been reading about Arrays, but I'm still stuck on how to do this or even start it. Thanks for the help guys.
Well, you know you need five elements in the array, so:
private var m_arr:Array = new Array(5);
That'll get you an array with five elements, which can be filled later.
Next, it sounds like you need to keep track of which number gets picked first, which number gets picked second, and so on. So let's make a place-holder and initialize it to 0:
private var m_iNextElement:int = 0;
Then whenever they select a number, just say:
m_arr[m_iNextElement] = theNumber;
m_iNextElement++;
As Fygo already mentioned, you could just do this instead:
private var m_arr:Array = new Array();
.
.
.
m_arr.push(number1);
.
.
.
m_arr.push(number2); // and so on
Whichever is better depends on the situation. Last, you need to find some way to tie each element of the array to a graphical element of some sort. There are many ways you could do this, but if you're using MXML, you could consider making the array bindable:
[Bindable]
private var m_arr:Array = new Array(5);
Then you could have in the MXML:
<local:Box id="box1" text="{m_arr[0]}" />
<local:Box id="box2" text="{m_arr[1]}" /> <!-- etc. -->
var nums:Array = [];
//when the user picks whatever number, you call:
nums.push(the_number_that_the_user_selected);
Is that all you wanted to achieve?
Actionscript offers your basic index array [] and associative array {} as well as some fancier ones like vector. It sounds like you simply need to do something with an index array.
Check out this tutorial, it's all about shuffling arrays. http://code.tutsplus.com/tutorials/quick-tip-how-to-randomly-shuffle-an-array-in-as3--active-8776
I'm making a Backbone app now and using a backbone.localstorage plugin to persist the data. My app has some sortable items so I hope every time I sort the items, the data order in localStorage will also change. And next time I refresh the whole page, the page will be rendered by the sorted data. But it seems that backbone.localstorage will persist the data in its creation order. Could someone give me some ideas on this?
If you want your models to appear in an explicit order then include a comparator in your collection and, possibly, a position number in each model.
Local storage is:
a means through which string key/value pairs can be securely stored and later retrieved for use.
Note the key/value pairs, that means that you're dealing with, more or less, a big hash table and those are usually unordered. Furthermore, from the fine specification:
The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn't change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.)
So there is no particular order inside local storage. If you want a specific order, you have to arrange for it yourself.
In your case, you'd probably have a position or index property in your models that would behave like an array index; then, in your collection:
comparator: function(m) { return m.get('position') } // or 'index'
You could also use a two argument comparator function:
comparator: function(a, b) {
a = a.get('position');
b = b.get('position');
if(a < b)
return -1;
else if(a > b)
return 1;
return 0;
}
You'd have to maintain the position indexes as you move models around but that shouldn't be terribly difficult. You could also order the data by position after pulling it out of local storage but before putting it in your collection and then assign position values while writing the models into local storage.