I honestly don't even know how to search for this question (what search param to write) but either way its bit weird issue and I am desperate for help.
So I am trying to do something simple, event sends "form-change" and when it does, we set new value in "this.data" object. Fairly simple. I don't expect this.data to be reactive I just want to update it.
// Find our data object which we want to update/change
if (form.field.includes('.')) {
let find = form.field.split('.'), level = this.data;
for (let index = 0; index < find.length; index++) {
if (level[find[index]] !== undefined) {
level = level[find[index]];
} else {
level = undefined;
}
}
if (level !== undefined)
level = setFieldData();
}
This is fairly simple, we have name of field "inspect.book" and when update comes (Event) we just use dots to split into multi tree and update "this.data.inspect.book" to new value. But it does not work. Value does not change.
But the value from actual this.data.inspect.book comes out just fine using:
console.log(level);
However, if I do this:
this.data[ form.field.split( '.' )[ 0 ] ][ form.field.split( '.' )[ 1 ] ] = setFieldData();
It works fine. So "reference" to variable does not work... how is this possible? Looks like a bug in javascript or is it something with vue/reactivity?
Does anyone have better idea how to get this to work?
So you are trying to update form data using to notation ?
i would do something like that :
_update(fieldName, value) {
// We split segments using dot notation (.)
let segments = fieldName.split(".");
// We get the last one
let lastSegment = segments.pop();
// We start with this.data as root
let current = this.data
if(current) {
// We update current by going down each segment
segments.forEach((segment) => {
current = current[segment];
});
// We update the last segment once we are down the correct depth
current[lastSegment] = val;
}
},
if i take your example :
if (form.field.includes('.')) {
let find = form.field.split('.'), level = this.data;
for (let index = 0; index < find.length - 1; index++) {
if (level[find[index]] !== undefined) {
level = level[find[index]];
} else {
level = undefined;
}
}
if (level !== undefined)
level[find.pop()] = setFieldData();
}
i replaced find.length by find.length - 1
and replaced level = setFieldData() by level[find.pop()] = setFieldData()
you should update the property of the object, without actually overriding the value,
because if you override the value, the original value will not get updated.
Related
Maybe ReSharper or visual studio is wrong, but I don't think that this returns an r-value. I also don't think it actually sets the property in the $parent controller:
function getParentItem(path) {
var obj = $scope.$parent;
var param = null;
var items = path.split(".");
for (var i = 0; i < items.length; i++) {
var item = items[i];
var split = item.split("(");
if (split.length === 2) {
param = split[1].replace(/[\)\']/g, "");
}
obj = obj[split[0]];
}
if (param == null) {
var thisObj = obj;
return thisObj;
} else {
return { obj: obj, param: param };
}
}
If I do this:
getParentItem($scope.someProperty) = "yadda"
I get error marked by probably ReSharper and I think it doesn't actually set the new value
As Amy/Volkan said your code is not valid but I think I get what you want to do. There are lots of ifs but here it goes:
if your $scope.someProperty is string property that you want to reassign on result of the function getParentItem, and your function returns object that can have that param ($scope.someProperty), first you need to figure out which path you pass in but it looks like it's some string separated by dots.
// so then assign result of the function to some variable
// you need to pass somePath to function
let parentItem = getParentItem(somePath);
// then change that property
parentItem[$scope.someProperty] = "yadda";
or another possibility what you might need would be:
parentItem.param[$scope.someProperty] = "yadda";
then do whatever you want with parentItem like put it on $scope or whatever.
If you want better help please do some jsfiddle or something.
The problem is (and I slap my head on how stupid I was) that the leaf branches of this $scope object aren't objects themselves, and in some cases in our code they don't even exist yet. You get so used to $scope being an object you fail to realize that the final elements can't possibly be objects at least in Javascript.
So the solution was to pass the value that I wanted to set as a parameter:
function getParentItem(path, optionalValue)
On the final loop of the parent search, if optionalValue is passed, I can then set the value onto the object:
obj[--last parameter name--] = optionalValue;
Is there a technique / framework, which creates and stores a new object into an undefined variable when it is handled as an object?
F.e. writing
some.thing = 7;
Gives an error describing, that undefined's ( some's ) property could not be set.
So now, we have to go back, and write
some = {};
some.thing = 7;
which looks almost unnecessary to me, since it is obvious, that I'd like to use some as an object.
It would be very comfortable this way:
hospitals.sectors.doctors = [];
hospitals.sectors.doctors[ 0 ].age = 32;
This looks way more efficient, and simple than
hospitals = {};
hospitals.sectors = {};
hospitals.sectors.doctors = [];
hospitals.sectors.doctors[ 0 ] = {};
hospitals.sectors.doctors[ 0 ].age = 32;
If you are trying to assign 7 to some.thing and some should not be declared at all beforehand, use Cory's suggestion of var some = { thing: 7 };.
If you are trying to assign 7 to some.thing and some might be declared/defined already (or even if it is declared, may or may not be set to undefined), use
(some = (some || {})).thing = 7;
or (a more readable solution, though not a one liner)
some = (some || {});
some.thing = 7;
The one caveat with my solution is that you might get undesired scoping on some if it isn't declared ahead of time. An example of where it would be undefined but still declared (and thus the scope would be what is expected):
function assignThing(some) {
(some = (some || {})).thing = 7; /* one liner solution */
return some;
}
var foo = assignThing(); /* the parameter 'some' is undefined, but declared */
var bar = assignThing({}); /* the parameter 'some' is defined
* (though empty) and declared */
(baz = (baz || {})).thing = 7; /* Unless you have this declared
* elsewhere this *should* be the
* global scope */
var foobar; /* declared, not defined */
(foobar = (foobar || {})).thing = 7; /* now defined */
Edit: Created a namespace solution like what anddoutoi suggested.
First example of usage (hospitals) uses the optional nature of the third parameter, which defaults to "undefined".
Second example (hospitals2) passes undefined.
Third example (hospitals3) passes another object (I happened to create it inline).
For the example you (the OP) provided, my hospitals example is the closest to an exact match. I don't know of a way to auto-magically create an object hierarchy besides using a canonical name string + a function.
function create(canonicalPropertyName, value, root) {
root = (root || {});
var names = canonicalPropertyName.split(".");
var current = root;
for (var i = 0; i < names.length; i++) {
/* Ensure the property exists */
current[names[i]] = (current[names[i]] || (i < names.length - 1 ? {} : []));
/* We're recursing down the object tree */
current = current[names[i]];
}
return root;
}
var hospitals = create("sectors.doctors", []);
hospitals.sectors.doctors[ 0 ] = {};
hospitals.sectors.doctors[ 0 ].age = 32;
console.log(hospitals);
var hospitals2 = create("sectors.doctors", [], undefined);
hospitals2.sectors.doctors[ 0 ] = {};
hospitals2.sectors.doctors[ 0 ].age = 32;
console.log(hospitals2);
var hospitals3 = create("sectors.doctors", [], { name: "City Hospital" });
hospitals3.sectors.doctors[ 0 ] = {};
hospitals3.sectors.doctors[ 0 ].age = 32;
console.log(hospitals3);
Many frameworks have a namespace() method/function that helps you achieve what you want.
http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_namespace
https://docs.sencha.com/extjs/5.1/5.1.1-apidocs/#!/api/Ext-method-namespace
Example:
Ext.ns('hospitals.sectors.doctors') = [];
var doctors = Ext.ns('hospitals.sectors.doctors');
doctors[0] = {
age : 32
};
I myself don't like this pattern as it adds unnecessary dependencies and work.
you could write an own handler to do the job
function createObjectProperty(objNameString, objPropertyString, objValueString) {
window[objNameString] = {};
window[objNameString][objPropertyString] = objValueString;
return window[objNameString];}
var test = createObjectProperty("some", "thing", 7);
alert(test.thing);
the call would look like:
createObjectProperty("some", "thing", 7);
and it alerts "7".You also could check before creating the new object, if it exists. I would iterate over the object and gather all attributes, check for duplicate and append my new attribute + value if you need such functionality. Otherwise the new object will overwrite the old one. Hope this is what you are searching for
working fiddle (update):
https://jsfiddle.net/a922bopj/1/
To clarify a bit on the question, I have an array and within the array are a bunch of objects. Can I rearrange the objects in the array based on the key values of each object?
When I'm attempting to do so, it keeps telling me that the variable (in this case, students) is undefined.
When I use the built-in sort function, it works perfectly. However, this is a school assignment and I HAVE to show the breakdown of the quicksort function.
Here is the code I am using:
function swap(student, firstIndex, secondIndex){
var temp = student[firstIndex];
student[firstIndex] = student[secondIndex];
student[secondIndex] = temp;
}
function partition(student, left, right) {
var pivot = student[Math.floor((right + left) / 2)],
i = left,
j = right;
while (i <= j) {
while (student[i] < pivot) {
i++;
}
while (student[j] > pivot) {
j--;
}
if (i <= j) {
swap(student, i, j);
i++;
j--;
}
}
return i;
}
function quickSort(student, left, right) {
var index;
if (student.length > 1) {
left = typeof left != "number" ? 0 : left;
right = typeof right != "number" ? student.length - 1 : right;
index = partition(student, left, right);
if (left < index - 1) {
quickSort(student, left, index - 1);
}
if (index < right) {
quickSort(student, index, right);
}
}
return student;
}
var studentNumbersArray = []
var randomArray = [];
for (i = 0; i < studentsArray.length; i++) {
studentNumbersArray.push(studentsArray[i].studentNumber);
}
function sortStudentNumbers() {
var student = studentNumbersArray.name; // <-- THIS IS WHERE I AM GETTING ERROR
quickSort(student);
var updatedStudentArray = JSON.stringify(studentsArray);
localStorage.setItem("students", updatedStudentArray);
location.reload();
}
After seeing several permutations of your code I think what you are trying to do needs to look a little something like this.
quickSort(arrayToSort, attributeToSortOn, left, right) {
...
}
...
function partition(arrayToSort, attributeToSortOn, left, right) {
var pivot = student[Math.floor((right + left) / 2)][attributeToSortOn]
...
while (arrayToSort[i][attributeToSortOn] < pivot) {
...
}
...
quickSort(studentsArray, 'studentNumber');
quickSort always needs the array to compare values at each position. You can't just pass studentsArray.studentNumber because the attribute to sort on is useless on it's own and the array has no knowledge of the types of object contained within it anyway.
You cannot use a variable defined inside a function, in another function. Every function has its own scope, and in order to carry data between functions (inside the variables) you have to create a variable in the upper (global) scope. That means, you have to create the student and updatedStudentArray outside of the functions and use them without declaring in the functions (sortStudentNumbers() in this case)
var student;
var updatedStudentArray;
function swap(student, firstIndex, secondIndex){
var temp = student[firstIndex];
student[firstIndex] = student[secondIndex];
student[secondIndex] = temp;
}
// ..................................
// ------ removed for clearity
// ..................................
function sortStudentNumbers() {
student = studentNumbersArray.name; // <-- THIS IS WHERE I AM GETTING ERROR
quickSort(student);
updatedStudentArray = JSON.stringify(studentsArray);
localStorage.setItem("students", updatedStudentArray);
location.reload();
}
These the mistakes I found. But still, when I execute this code I get a studentsArray is not defined error on the console. Where is your studentsArray? If you have it somewhere, then it should all work now.
EDIT:
After you created the second question, I had a quick chance to have a look at your modified code. Don't forget to update your question here.
My Answer to your modified code:
You have to set a "student" key in localStorage first, in order to use getItem() for it. You don't have anything in the localStorage, that is why your variable is not filled when you try to get the data from there. Thus, you are getting the error "Cannot read property 'studentNumber' of null".
i've got an array, and i want to shuffle those according to a certain pattern
(i'm trying to make a rubics cube in javascript).
I want to assign value2 to value 1 and value 1 to value 3 and value 3 to value 2. I can do that within 4 lines of code, but is there a shorter way?
like:
temp = var3; //make temporary variable
(var3 = var2) = var1;//put var2 in var3 and var3 in var1
var1 = temp;//put var3/temp in var1
i know that it doesn't work this way, but do you guys know a way it does work?
that would be usefull when cycling 8 variables.
thanks,
Tempestas Ludi.
If you're dealing with more than 2 variables, it's always best to use an array, since arrays have built in functions you can use
var nums = [1,2,3]; // this is your array. it can have any length you want
// this is one line that does the magic
nums.push(nums.shift()); // to the left
nums.unshift(nums.pop()); // to the right
http://jsfiddle.net/wbKYY/2/
http://jsfiddle.net/wbKYY/4/
Anyway, about your comment. Seeing as the pointers which you will rotate aren't predetermined, and will vary, it's probably best to use a function.A function that will iterate through pointers that you define.
function rotateArr(arr,pointers,dir) {
var narr = arr; // create a local copy we can use it
var savevalue;
if (dir == 1) { // rotate to the right
savevalue = narr[pointers[pointers.length-1]]; // save the last value
for(var i=pointers.length-2;i>=0;i--) {
narr[pointers[i+1]] = narr[pointers[i]];
}
narr[pointers[0]] = savevalue; // set the first value
} else { // rotate to the left
savevalue = narr[pointers[0]]; // save the first value
for(var i=0;i<pointers.length-1;i++) {
narr[pointers[i]] = narr[pointers[i+1]];
}
narr[pointers[pointers.length-1]] = savevalue; // set the last value
}
return narr;
}
// arr is the array of faces
// pointers is the elements which you want to rotate (an array)
// dir is either 1 or -1
you can execute this function with
nums = rotateArr(nums,[pointers],[1,-1]);
// just some examples
nums = rotateArr(nums,[0,1,2,5],1);
nums = rotateArr(nums,[3,6,1,4],-1);
Here's a working example:
http://jsfiddle.net/w2jGr/
However if you prefer to use a prototype function that is just a method of an array, you can define a property and just access it from there.
Object.defineProperty(Object.prototype, "rotateArr", { value: function(pointers,dir) {
var savevalue;
if (dir == 1) { // rotate to the right
savevalue = this[pointers[pointers.length-1]]; // save the last value
for(var i=pointers.length-2;i>=0;i--) {
this[pointers[i+1]] = this[pointers[i]];
}
this[pointers[0]] = savevalue;
} else { // rotate to the left
savevalue = this[pointers[0]]; // save the last value
for(var i=0;i<pointers.length-1;i++) {
this[pointers[i]] = this[pointers[i+1]];
}
this[pointers[pointers.length-1]] = savevalue;
}
}, enumerable : false});
Modifying the prototype of an object is never recommended, but if you aren't going for clean code and want usability or readability, this works great as you can call the function with
nums.rotate([pointers],dir);
http://jsfiddle.net/w2jGr/1/
Why not put your values in an array, and just change the index pointer when you want to use different values?
I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.