i don't understand arrays in functions, but how do you find the matching items given in an array? for example:
var fruit:Array = ["apples", "oranges", "grapes", "oranges", "apples", "grapes"];
how can i get it to show only the number of apples in the array?
Simply, you can do this:
private function getCount(fruitArray:Array, fruitName:String):int {
var count:int=0;
for (var i:int=0; i<fruitArray.length; i++) {
if(fruitArray[i].toLowerCase()==fruitName.toLowerCase()) {
count++;
}
}
return count;
}
var fruit:Array = ["apples", "oranges", "grapes", "oranges", "apples", "grapes"];
var appleCount=getCount(fruit, "apples"); //returns 2
var grapeCount=getCount(fruit, "grapes"); //returns 2
var orangeCount=getCount(fruit, "oranges"); //returns 2
Depending on how many items are in the array, and how often you want to search for different items, it can get very costly to have a for loop iterate over the entire array, every time you want to count things (which would happen if you did what the other two answers suggest). You might want to have a count function that iterates over the array just once, and returns a list of counters, instead of a single number:
function countItems( arr:Array ) : Object {
var counts : Object = {};
for each ( var item : String in arr ) {
if (counts.hasOwnProperty(item)) counts[item]++;
else counts[item] = 1;
}
return counts;
}
var fruit:Array = ["apples", "oranges", "grapes", "oranges", "apples", "grapes"];
var counts : Object = countItems(fruit);
trace (counts["apples"]); // => 2
trace (counts["oranges"]); // => 2
trace (counts["grapes"]); // => 2
function countOccurrences( haystack, needle ){
var index : int = -1;
var counter : int = -1;
do{
counter++;
index = haystack.indexOf( needle, index + 1 );
}while( index >= 0 );
return counter;
}
var fruit:Array = ["apples", "oranges", "grapes", "oranges", "apples", "grapes"];
trace( countOccurrences( fruit, "apples" ) ); //outputs '2'
var stringToSearch:String = "apples";
var pattern:RegExp = new RegExp(/\b\b/.source+stringToSearch, "g");
var matches:Array = fruit.toString().match(pattern);
//
trace(matches.length);
Disclaimer: don't use this if you have many elements in your array (eg. fruit.length>1000): it is compact but it can be slower than any other solution posted here. Another thing to point out is that here I'm assuming your "matching items" are "strings".
Try something like
private function makeFilter(filterValue:String):Function {
var fn:Function = function (obj:Object, index:int=0, arr:Array=[]):Boolean {
if (String(obj)==filterValue) {
return true;
}
return false;
}
return fn;
}
//somewhere else:
var filterFunction:Function = makeFilter('apples');
var appleCount:int = fruit.filter(filterFunction).length;
FYI, more about this approach: http://www.gasi.ch/blog/functional-actionscript-1/
var matches = 0
var arrayMatcher = (array1, array2) => {
for (let i = 0; i <= array1 - 1; i++) {
var myFirstItem = array1[i];
for (a = 0; a <= array2.length - 1; a++) {
var mySecondItem = array2[a];
if (mySecondItem === myFirstItem) {
matches += 1;
}
}
}
return matches;
}
I used this to find matches in a list of a patient's symptoms with a list of common symptoms for a certain disease and it worked for me. You could give it a try! :)
Related
I want to bind my ng-model with JSON object nested key where my key is in a variable.
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']"
Here I want to get value 5 from data JSON object.
I found the solution to convert "course.sections.chapter_index" to array notation like course['sections']['chapter_index'] this. but don't know how to extract value from data now
<script type="text/javascript">
var BRACKET_REGEXP = /^(.*)((?:\s*\[\s*\d+\s*\]\s*)|(?:\s*\[\s*"(?:[^"\\]|\\.)*"\s*\]\s*)|(?:\s*\[\s*'(?:[^'\\]|\\.)*'\s*\]\s*))(.*)$/;
var APOS_REGEXP = /'/g;
var DOT_REGEXP = /\./g;
var FUNC_REGEXP = /(\([^)]*\))?$/;
var preEval = function (path) {
var m = BRACKET_REGEXP.exec(path);
if (m) {
return (m[1] ? preEval(m[1]) : m[1]) + m[2] + (m[3] ? preEval(m[3]) : m[3]);
} else {
path = path.replace(APOS_REGEXP, '\\\'');
var parts = path.split(DOT_REGEXP);
var preparsed = [parts.shift()]; // first item must be var notation, thus skip
angular.forEach(parts, function (part) {
preparsed.push(part.replace(FUNC_REGEXP, '\']$1'));
});
return preparsed.join('[\'');
}
};
var data = {"course":{"sections":{"chapter_index":5}}};
var obj = preEval('course.sections.chapter_index');
console.log(obj);
</script>
Hope this also help others. I am near to close the solution,but don't know how can I get nested value from JSON.
This may be a good solution too
getDeepnestedValue(object: any, keys: string[]) {
keys.forEach((key: string) => {
object = object[key];
});
return object;
}
var jsonObject = {"address": {"line": {"line1": "","line2": ""}}};
var modelName = "address.line.line1";
var result = getDescendantPropValue(jsonObject, modelName);
function getDescendantPropValue(obj, modelName) {
console.log("modelName " + modelName);
var arr = modelName.split(".");
var val = obj;
for (var i = 0; i < arr.length; i++) {
val = val[arr[i]];
}
console.log("Val values final : " + JSON.stringify(val));
return val;
}
You are trying to combine 'dot notation' and 'bracket notation' to access properties in an object, which is generally not a good idea.
Source: "The Secret Life of Objects"
Here is an alternative.
var stringInput = 'course.sections.chapter_index'
var splitInput = stringInput.split(".")
data[splitInput[1]]][splitInput[2]][splitInput[3]] //5
//OR: Note that if you can construct the right string, you can also do this:
eval("data[splitInput[1]]][splitInput[2]][splitInput[3]]")
Essentially, if you use eval on a string, it'll evaluate a statement.
Now you just need to create the right string! You could use the above method, or tweak your current implementation and simply go
eval("data.course.sections.chapter_index") //5
Source MDN Eval docs.
var data = {
"course": {
"sections": {
"chapter_index": 5
}
}
};
var key = "course['sections']['chapter_index']";
var keys = key.replace(/'|]/g, '').split('[');
for (var i = 0; i < keys.length; i++) {
data = data[keys[i]];
}
console.log(data);
The simplest possible solution that will do what you want:
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']";
with (data) {
var value = eval(key);
}
console.log(value);
//=> 5
Note that you should make sure key comes from a trusted source since it is eval'd.
Using with or eval is considered dangerous, and for a good reason, but this may be one of a few its legitimate use cases.
If you don't want to use eval you can do a one liner reduce:
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']"
key.split(/"|'|\]|\.|\[/).reduce((s,c)=>c===""?s:s&&s[c], data)
As we know its possible to access field by name using indexer.
var obj:* = {name:"Object 1"};
trace(obj["name"]); // "Object 1"
But how to access an array element by String?
var arr:Array = new Array();
var obj:* = {items:arr};
trace(obj["items[0]"]); // Undefined
Ok, basically you want to be able to have a string be interpreted as actionscript. No elegant solution I'm afraid. You could write a parser that handles some simple syntax in a string and retrieves the value.
Here's a simple example:
var obj:Object = {
items:[1, 2, 3],
subObj: {
subitems: [4, 5, 6]
}
};
trace(getValueInObject(obj, "items[0]")); // 1
trace(getValueInObject(obj, "subObj.subitems[2]")); // 6
// takes an object and a "path", and returns the value stored at the specified path.
// Handles dot syntax and []
function getValueInObject(obj : Object, pathToValue : String) : * {
pathToValue = pathToValue.replace(/\[/g, ".").replace(/]/g, "");
var pathFractions : Array = pathToValue.split(".");
var currentObject : Object = obj;
while (pathFractions.length > 0 && currentObject != null) {
var fraction : String = pathFractions.shift();
currentObject = currentObject[fraction];
}
if (currentObject != null) {
return currentObject;
}
return null;
}
I have a button on page - when clicked, it passes all the data to the servlet that could update each row data. My question is how to pass the whole store to the servlet as json data? Is there any easy way? Thanks
Here is some code I wrote to get the store to an object. Then it can be converted to JSON using dojo.toJson(obj);. I learned about this from the dojotoolkit website originally. (Give credit where credit is due). I realize this code is huge and nasty. When I looked for a better way about a year back I could not find one.
JsonHelper.storeToObject = function(store) {
var object = [];
var index = -1;
store.fetch({
onItem : function(item, request) {
object[++index] = JsonHelper.itemToObject(store, item);
}
});
return object;
};
JsonHelper.itemToObject = function(store, item) {
// store:
// The datastore the item came from.
// item:
// The item in question.
var obj = {};
if (item && store) {
// Determine the attributes we need to process.
var attributes = store.getAttributes(item);
if (attributes && attributes.length > 0) {
var i;
for (i = 0; i < attributes.length; i++) {
var values = store.getValues(item, attributes[i]);
if (values) {
// Handle multivalued and single-valued attributes.
if (values.length > 1) {
var j;
obj[attributes[i]] = [];
for (j = 0; j < values.length; j++) {
var value = values[j];
// Check that the value isn't another item. If
// it is, process it as an item.
if (store.isItem(value)) {
obj[attributes[i]].push(itemToObject(store,
value));
} else {
obj[attributes[i]].push(value);
}
}
} else {
if (store.isItem(values[0])) {
obj[attributes[i]] = itemToObject(store,
values[0]);
} else {
obj[attributes[i]] = values[0];
}
}
}
}
}
}
return obj;
};
Hopefully this is easy but that sometimes means its impossible in flex and I have searched quite a bit to no avail.
Say I have a list (LIST#1) of artists:
2Pac
Adele
Amerie
Beyonce
Jason Aldean
Shakira
The Trews
I also have a list (LIST#2) that has the values #,A-Z - how would I create an alphabet jump?
So If a user clicked on "A" in LIST#2 that would automatically scroll to "Adele" at the top of LIST#1 - not filter so he/she could scroll up to view 2Pac or down to view The Tews if they were not in the view yet.
Its a standard Flex Spark List with an ArrayCollection as the dataProvider - the artist field is called: "title" along with a unique id field that is not visible to the user.
Thanks!
Please see comments on marker answer for discussion on Dictionary that may be faster in some cases. See below for code (HAVE NOT CONFIRMED ITS FASTER! PLEASE TEST):
private function alphabet_listChange(evt:IndexChangeEvent) : void {
var letter:String;
letter = evt.currentTarget.selectedItems[0].toString();
trace(currentDictionary[letter]);
ui_lstLibraryList.ensureIndexIsVisible(currentDictionary[letter]);
}
public function createAlphabetJumpDictionary() : Dictionary {
//alphabetArray is a class level array containing, A-Z;
//alphabetDictionary is a class level dictionary that indexes A-z so alphabetDictionary["A"] = 0 and ["X"] = 25
var currentIndexDict:Dictionary = new Dictionary; //Dictionary is like an array - just indexed for quick searches - limited to key & element
var searchArray:Array = new Array;
searchArray = currentArrayCollection.source; //currentArrayCollection is the main array of objects that contains the titles.
var currentIndex:Number; //Current index of interation
var currentAlphabetIndex:Number = 0; //Current index of alphabet
for (currentIndex = 0; currentIndex < searchArray.length; currentIndex++) {
var titleFirstLetter:String = searchArray[currentIndex].title.toString().toUpperCase().charAt(0);
if (titleFirstLetter == alphabetArray[currentAlphabetIndex]) {
currentIndexDict[titleFirstLetter] = currentIndex;
trace(titleFirstLetter + " - " + currentIndex);
currentAlphabetIndex++;
} else if (alphabetDictionary[titleFirstLetter] > alphabetDictionary[alphabetArray[currentAlphabetIndex]]) {
trace(titleFirstLetter + " - " + currentIndex);
currentIndexDict[titleFirstLetter] = currentIndex;
currentAlphabetIndex = Number(alphabetDictionary[titleFirstLetter] + 1);
}
}
return currentIndexDict;
}
private function build_alphabeticalArray() : Array {
var alphabetList:String;
alphabetList = "A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z";
alphabetArray = new Array;
alphabetArray = alphabetList.split(".");
return alphabetArray;
}
private function build_alphabetDictionary() : Dictionary {
var tmpAlphabetDictionary:Dictionary = new Dictionary;
for (var i:int=0; i < alphabetArray.length; i++) {
tmpAlphabetDictionary[alphabetArray[i]] = i;
trace(alphabetArray[i] + " - " + i);
}
return tmpAlphabetDictionary;
}
private function buildCurrentDictionary() : void {
trace("Collection Changed");
currentDictionary = new Dictionary;
currentDictionary = createAlphabetJumpDictionary();
}
The Flex Spark list has a very convenient method called ensureIndexIsVisible(index). Check the Flex reference documentation. All you have to do is to find the index of the first artist for the corresponding selected alphabet letter:
public function findAlphabetJumpIndex():Number
{
var jumpToIndex:Number;
var selectedLetter:String = alphabethList.selectedItem;
for (var i:int=0; i < artists.length; i++)
{
var artistName:String = artists.getItemAt(i);
var artistFirstLetter:String = artistName.toUpperCase().charAt(0);
if (artistFirstLetter == selectedLetter)
{
jumpToIndex = i;
break;
}
}
return jumpToIndex;
}
You can iterate your artist list data provider and check if artist name starts with selected alphabet from list two. When corresponding artist is found, set artist list selected index a value what you get from iterating data.
var asdf:Array = [ [1,1] ];
trace( asdf.indexOf( [1,1] ) ); // -1
Why can't indexOf() find the [1,1] array?
Here is a little function I wrote a while ago that works great. I included a lot of comments and an example search/function to output the results.
// set up a multidimensional array that contains some data
var myArray:Array = new Array();
myArray.push(["granola","people... are great"," 4 ","10"]);
myArray.push(["bill","orangutan","buster","keaton"]);
myArray.push(["steve","gates","24","yes, sometimes"]);
myArray.push(["help","dave","jobs","hal"]);
// here we set up some properties on the array object to hold our search string and our results
myArray.myTarget = "steve";
myArray.myResults = [];
// now we call the search
myArray.forEach(multiSearch);
// this is the function that does all the heavy lifting....
function multiSearch(element:*, index:int, array:Array)
{
// see if we have a match in this array and pass back its index
for(var i:* in element)
{
if( element[i].indexOf( array.myTarget ) > -1 )
{
var tempArray:Array = array.myResults;
tempArray.push([index,i]);
array.myResults = tempArray;
}
}
}
// -------------------------------------------------------------------------------
// all the code below is OPTIONAL... it is just to show our results
// in the output window in Flash so you know it worked....
var printArray:Array = myArray.myResults;
for(var i:* in printArray)
{
trace("TARGET FOUND #: "+printArray[i][0]+", "+printArray[i][1]+" = "+myArray[ printArray[i][0] ][ printArray[i][1] ]);
}
// -------------------------------------------------------------------------------
It fails because when you do a [x,y] you are creating a new array, adsf contains one array and indexOf search for another one.
try:
trace([1,1] == [1,1]);
You will see that it prints false, since array are compare by reference.
One quick indexOf function, arrange it to suit your needs:
function isElmEquals(e1:*, e2:*):Boolean {
return (e1==e2);
}
function isArrayEquals(a1:Array, a2:Array):Boolean {
if (a1==a2)
return true;
if ((a1==null) || (a2==null)) {
return false;
}
if (a1.length!=a2.length)
return false;
for (var i:int=0;i<a1.length;i++){
if (!isElmEquals(a1[i], a2[i]))
return false;
}
return true;
}
function indexOf(value:Array, into:Array):int{
var i:int = -1;
into.some(
function(item:*, index:int, array:Array):Boolean {
if (isArrayEquals(item as Array, value)) {
i = index;
return true;
}
return false;
}
);
return i;
}
var i:int=indexOf([1,1], [[-1,1], [0,1], [1,1], [1,-1]]);
trace(i);
var j:int=indexOf([1,2], [[-1,1], [0,1], [1,1], [1,-1]]);
trace(j);
this works. probably because the inner array is typed.
var qwer:Array = [1,1];
var asdf:Array = [qwer];
trace( asdf.indexOf( qwer ) ); // 0