I have already tried the solutions here:
This: knockout arrayfirst not working as expected
This: ko.utils.arrayFirst always returns null when not handling else block with non-empty string
This: Knockout Check If Existing in observableArray before Push
But nothing works for me. I am checking if an item is already existing in the observable array
ko.utils.arrayForEach(self.Summary(), function (item) {
var match = ko.utils.arrayFirst(self.filteredSummary(), function (a) {
return a.Sku == item.Sku()
});
if (!match) {
// Do push
}
});
Am I doing something wrong? This always returns null even though when debugged, it founded a match.
I attached the snippet of the values:
Check the statement,
return item.Sku() === a.Sku()
=== : equal value and equal type,
== : equal to,
https://www.w3schools.com/js/js_operators.asp
In your case both the value and the type of the two summary objects must be equal.
Ok, try this one
ko.utils.arrayForEach(self.Summary(), function (item) {
var match = ko.utils.arrayFirst(self.filteredSummary(), function (a) {
return a.Sku() == item.Sku();
});
if (!match) {
// Do push
}
});
If this workd, the problem was that a.SKu was an observable and you were not evaluating it! Read my comment on your original question
Related
I have a big array of objects where I need to get the unique values for some keys.
I have the code working, but I would like to understand it.
Object
{
"cor": {
"id": 89,
"code": "192"
},
"tamanho": {
"id": 74,
"code": "L"
},
"price": "56,34",
"prevPrice": "93,90",
"stock": 0
}
And this is the iteration that return only unique values.
What I can't understand is the return statement, how does javasacript reads it?
var tamanhos = [];
data.grelha.filter(function(el, i, arr) {
return tamanhos.indexOf(el.tamanho.id) == -1 && tamanhos.push(el.tamanho.id);
});
Thanks a lot!
Before I get to what I think you're asking, let's talk about .filter(). Firstly, it returns a new array, so calling .filter() without using its return value is not how you are supposed to use it: if you just want to iterate over the array you should use .forEach() instead. It works by calling the function you pass it once per array element. Only elements for which your function returns a truthy value will be added to the output array. The correct way to use .filter() to solve this problem would be something like this:
var tamanhos = data.grelha.map(el) { return el.tamaho.id }).filter(function(el, i, arr) {
return arr.indexOf(el) === i
})
That is, first use .map() to get a list of all the IDs, then use .filter() to only keep elements if they are the first occurrence of that ID in the array, thus setting tamanhos to be an array of unique IDs.
Now to what you seem to be asking, which is for an explanation of what the following line is doing:
return tamanhos.indexOf(el.tamanho.id) == -1 && tamanhos.push(el.tamanho.id);
The key is the && (logical AND) operator. It uses short circuit evaluation, which means that the expression on the right-hand-side of the && will only be evaluated if the expression on the left-hand-side is truthy.
Two simple examples (click "Run"):
true && alert('This WILL display because alert() is called.');
false && alert('This will NOT display because alert() is not called');
So if tamanhos.indexOf(el.tamanho.id) == -1 is true then the tamanhos.push(el.tamanho.id) part will be executed, otherwise the .push() will not be executed. In other words, the part after the return is equivalent to doing this:
if (tamanhos.indexOf(el.tamanho.id) == -1) {
tamanhos.push(el.tamanho.id);
}
(That is, if the item isn't already in the tamanhos array then add it.)
The result of the whole && expression is then returned, but as mentioned above that is not really relevant because you don't use the return value from .filter(). So what your code is doing is equivalent to this:
var tamanhos = [];
data.grelha.forEach(function(el) {
if (tamanhos.indexOf(el.tamanho.id) == -1) {
tamanhos.push(el.tamanho.id);
}
});
this is a json datas. Json values can use array. I think, you know arrays. So Arrays has multi areas. Like :
[val1]
[val1, val2]
[val1, val2, val3]
[val1, val2, val3, .....]
we can take values as column name. Example for c#:
array[] names = new array["mesut", "joa"]
you have javascript arrays
The following function takes an object, loops through each value and returns false if the object or its children have an empty or undefined property web. Otherwise, it returns true:
hasNoCategories (object) {
for (let key in object) {
const value = object[key]
for (let i = 0; i < value.length; i++) {
const item = value[i]
if (item.web !== undefined && item.web !== '') return false
}
if (key === 'web' && value !== '') {
return false
}
}
return true
},
Example input:
{
"livingroom": [],
"garage": [],
"outdoors": [],
"other": [],
"id": "ZI4hteKxgr",
"name": "Cuiti",
"description": "",
"user": "",
"date": "2016/5/13",
}
How to rewrite this function without using for loops?
I'm not 100% sure what you expect the code to do, because your existing code and your description differ.
Your description is, rephrased, that this function checks whether object.web or any object.XXX.web are undefined. Your code however assumes that all members are arrays and checks whether object.web or object.XXX[YYY].web are undefined. (Note that it also doesn't do it correctly and accesses .length even though the member in question might be undefined.)
Since I'm not sure which of those is right, I'm providing two answers.
Functionality as per your textual description:
function hasNoCategories(object) {
if(!object.web) return false;
return Object.keys(object).every(function(key) {
if(typeof object[key] !== 'object') return true;
return !!object[key].web;
});
}
Functionality as per your existing code: (but with the length property access fixed)
function hasNoCategories(object) {
if(!object.web) return false;
return Object.keys(object).every(function(key) {
if(!Array.isArray(object[key])) return true;
return object[key].every(function(el) {
if(typeof object[key] !== 'object') return true;
return !!el.web;
});
});
}
To understand how this works, check out the documentation on Object.keys (which returns an array with the names of all keys in your object) and Array.prototype.every (which runs a callback function for every element in an array and returns true only if the callback returned true for every element).
Note that I'm assuming that your "empty or undefined" should reject all kinds of falsy values including null and the number (not string) zero. If not, then all the checks like if(!something) and return !!something would need to be changed to if(typeof something === "undefined" || something === '') and return typeof something !== "undefined" && something !== '', respectively.
Side note to prevent nitpicking: Of course there are still loops going on. But it was specifically asked "without for loop" and there is no for in this code.
I assume this is what you are looking for:
var hasNoCategories = function(object) {
if (!object.web) {
return false;
}
for (let key in object) {
var value = object[key];
if (!value.web) {
return false;
}
}
return true;
};
I got rid of 1 loop. But this cannot be done without loops because you have to loop over all children. You can hide this loop inside some another function but you cannot get rid of it.
If you really don't want to use loops (I don't know why), one of your options is to serialize the object and phrase the string for the word "web".
var s = JSON.stringify(object);
var webIndex = s.indexOf('web');
Now perform some checks around this index to ascertain if that has the value 'undefined' or ''. Please keep in mind that the word "web" can match as a part of another property name too. So, you need to include this possibility too to your checks.
i have a method that returns an object that contains 4 objects:
function getFiles() {
var documents = {};
documents.files1ToBeCompleted = DocumentsService.getFiles1Uncompleted();
documents.files2ToBeCompleted = DocumentsService.getFiles2Uncompleted();
documents.files3ToBeCompleted = DocumentsService.getFiles3Uncompleted();
documents.files4ToBeCompleted = DocumentsService.getFiles4Uncompleted();
return documents;
}
I'm trying to use Underscore function ._isEmpty to verify if the object is empty, i mean the case in which i get an object with empty sub-objects.
But even all its 4 objects are empty, it is not empty because it contains 4 items.
Do you know any way to check if an object is "deep empty"?
Here's what worked for me. It is recursive and takes care of all nested objects (uses lodash).
function isEmptyDeep(obj) {
if(isObject(obj)) {
if(Object.keys(obj).length === 0) return true
return every(map(obj, v => isEmptyDeep(v)))
} else if(isString(obj)) {
return !obj.length
}
return false
}
It first checks if there are no keys, and returns true in that case.
Then it checks the keys and runs isEmptyDeep on each. If the value is an object (or array), it will continue recursion.
If there's an empty array or empty string, length will be 0 and will be considered empty.
If the value is 0, false, or other falsy values, then it would be considered not empty. If you want to consider falsey values as empty, this as the first line in the function above:
if(!obj) return true
Thanks to Bergi that lead me to this working solution:
_.every(documentsObject, function(property) { return _.isEmpty(property); });
that returns true if the object is "deep empty", false otherwise.
I am using a function to go through an array of objects and filter out the one that matches certain criteria.
var thread = underscore.find(threads, function (th) {
function result(threadArray){
threadArray.forEach(function(threads){
if (threads.owner.id === ownerId && threads.name.toLowerCase() == threadName) {
console.log(threads)
return threads
}
});
};
console.log(result(th));
return th.owner.id === ownerId && th.name.toLowerCase() === threadName;
});
'th' is the array of objects. Stepping through it, I can see that the array of objects is being iterated over using the forEach function, and that my if logic is successfully filtering out just one object because I can see it in my console "console.log(threads)", but when I try to console the return value of the function by invoking it, "console.log(result(th))", it comes back as undefined, and I can't figure out why.
I dont want to push duplicate values into selectedOwners, so in below code user is selecting owner if owner already existed in selectedOwners array i dont want to push , How can i check that to avoid duplicate values in an array ?
ctrl.js
var selectedOwners = [];
$scope.addProcessOwner = function(dataItem){
var selectedOwner = {
fullName: dataItem.fullName,
workerKey: dataItem.workerKey
}
if(selectedOwners.indexOf(selectedOwner) !== -1) {
selectedOwners.push(selectedOwner);
}
console.log('WORKER DATA',selectedOwners);
}
You can use Array.prototype.some method
The some() method tests whether some element in the array passes the test implemented by the provided function.
var isExists = function(e) {
if (e.fullName == selectedOwner.fullName
&& e.workerKey == selectedOwner.workerKey) {
return true;
}
}
if (!selectedOwners.some(isExists)) {
selectedOwners.push(selectedOwner);
}
The use of Array.indexOf is obvious for simple types like strings and numbers.
However, when you are looking for an object, you have to pass the exact same object. A different object with all the same properties and values will still not work. Think of the array as containing pointers to the objects and you must look for the same pointer.
Instead you will need to write your own method to compare the owners for equality and loop through the array doing this check.
Try wrapping your "if" logic in a for-loop .
Example
//Following code loops through array and check for already existing value
for(var i = 0; i < selectedOwners.length; i++){
if(selectedOwners.indexOf(selectedOwner) !== -1) {
selectedOwners.push(selectedOwner);
}
}