Dynamic if conditions in JavaScript - javascript

Is there anyways to pass the condition for if statement dynamically along with its operator without using eval() or new function() that affects it's performance.
<script>
var Students = [{
"name": "Raj",
"Age":"15",
"RollNumber": "123",
"Marks": "99",
}, {
"name": "Aman",
"Age":"14",
"RollNumber": "223",
"Marks": "69",
},
{
"name": "Vivek",
"Age":"13",
"RollNumber": "253",
"Marks": "89",
}
];
*Number of condition are dynamic. can even contain 3 or 4 condition at a time*
var condition = [{"attribute":"el.Age","operator":">=","value":14},
{"attribute":"el.Marks","operator":"<=","value":70}];
var newArray =Students.filter(function (el)
{
if( condition[0].attribute condition[0].operator condition[0].value && condition[1].attribute condition[1].operator condition[1].value ){
return true;
});
console.log(newArray);
</script>

Yes, you pass the operands and the operator into a function that dispatches based on the operator to something that does the operation.
As a code sketch:
const operators = new Map([
["<", lt],
[">", gt],
["<=", lte],
[">=", gte],
// ...
]);
function evaluate(operator, leftOperand, rightOperand) {
const evaluator = operators.get(operator);
if (!evaluator) {
throw new Error(`Unknown operator ${operator}`);
}
return evaluator(leftOperand, rightOperand);
}
...where lt, gt, etc. are functions that perform the operation. I've used operator, leftOperand, and rightOperand above, but you could use attribute and value if you prefer. You'll need something like this to get the value of the "attribute" (the JavaScript term is "property").
(Or you could do the operations in the evaluate function itself in a big switch.)
Then you use that function in the filter:
let newArray = Students.filter(student => conditions.every(({operator, attribute, value}) => {
const leftOperand = getProperty(student, attribute);
return evaluate(operator, leftOperand, value);
}));
I've used every there because you're using an && condition. (Note I renamed the condition array to conditions since arrays hold more than one value.)

Related

javascript array push keeps repeating it self cant find out why

I cant figure out why this arrayUnd gets repeated key values. ive been on this for three weeks. I must be doing somehthing stupid. I know they are loops. but everything works except the push. it logs when the key repeats and stuff. but it somehow is adding it to it? very confusing.
Heres my Javascript
var array = [
{"size":["12","22"]},
{"color":["blue"]},
{"design":["flower-blue"]},
{"size":["12","22","44"]},
{"color":["red"]},
{"design":["flower-blue"]}
]
//output array
arrayUnd=[
{"color":["red"]}
]
//is array?
console.log( Array.isArray(array))
function pusher(obj){
arrayUnd.push(obj)
}
function isRepeat(key,value,obj){
// console.log(key[0])
for (let item=0; item < arrayUnd.length; item++ ){
if ( arrayUnd[item].hasOwnProperty(key)){
console.log("key: " + key)
console.log("yes")
console.log(arrayUnd)
}
else{
console.log("keyno: " + key)
console.log("no")
//pusher(obj)
if ( arrayUnd[item].hasOwnProperty(key) === false){
pusher(obj)
}
console.log(arrayUnd)
}
}
}
array.forEach((obj)=>{
var a= Object.keys(obj)
var b= Object.values(obj)
isRepeat(a,b,obj)
})
console.log(arrayUnd)
Your loop is checking all objects to see if they don't match. At least one of them won't match, so it will try to push that many times. You need to check all elements, and after checking them all for existence decide if you want to push or not.
Current logic is equivalent to: if anything in arrUnd doesn't match, push me each time I check.
some works here, because it checks if anything matches, and returns true or false, which you can then use to decide if you want to push or not (only once, after I've found if anything in the array matches, deciding using the final result).
Using some to check if any other element with same key exists. Push if nothing found.
var array = [{
"size": ["12", "22"]
},
{
"color": ["blue"]
},
{
"design": ["flower-blue"]
},
{
"size": ["12", "22", "44"]
},
{
"color": ["red"]
},
{
"design": ["flower-blue"]
}
]
//output array
arrayUnd = [{
"color": ["red"]
}]
//is array?
console.log(Array.isArray(array))
function pusher(obj) {
arrayUnd.push(obj)
}
function isRepeat(key, value, obj) {
if(!arrayUnd.some(x => x.hasOwnProperty(key[0])))
arrayUnd.push(obj)
}
array.forEach((obj) => {
var a = Object.keys(obj)
var b = Object.values(obj)
isRepeat(a, b, obj)
})
console.log(arrayUnd)
You are passing an array to isRepeat instead of the key and value of the object.
Object.keys() returns an array, even if the object only has one key.
When you check if ( arrayUnd[item].hasOwnProperty(key) === false), arrayUnd[item].hasOwnProperty(key) will always be false, so the object will always get pushed to your array.
You can fix this by accessing the first key and value of each object:
array.forEach((obj)=>{
var a= Object.keys(obj)
var b= Object.values(obj)
isRepeat(a[0],b[0],obj)
})
The reason your code keeps pushing object to arrayUnd it's because when it iterates through the array it checks for the array key if its present if not it pushes it to arrayUnd, now you have 2 problems first you are not actually checking for the array key to match you comparing object so you will always get false , and second is that each time you push to the array the length of your array grow and so the number of iterations increases
you can achieve this in two lines of code
var array = [
{"size":["12","22"]},
{"color":["blue"]},
{"design":["flower-blue"]},
{"size":["12","22","44"]},
{"color":["red"]},
{"design":["flower-blue"]}
]
//output array
arrayUnd=[
{"color":["red"]}
]
array.forEach(p=>Object.entries(p).forEach(p=>{
!arrayUnd.some(o=>o.hasOwnProperty(p[0])) ? arrayUnd.push({[p[0]]:p[1]}):null
}))
console.log(arrayUnd)
var inpAry = [
{ "size": ["12", "22"] },
{ "color": ["blue"] },
{ "design": ["flower-blue"] },
{ "size": ["12", "22", "44"] },
{ "color": ["red"] },
{ "design": ["flower-blue"] }
];
var opAry = [
{ "color": ["red"] }
];
inpAry.forEach(inpAryElem => {
var ipAryElemKeys = Object.keys(inpAryElem);
var ipAryElemVals = Object.values(inpAryElem);
ipAryElemKeys.forEach((ipAryElmKey,ipAryElemKyIdx) => {
var isKeyPresent = false;
opAry.forEach(opAryElem => {
if(opAryElem[ipAryElmKey]) {
isKeyPresent = true;
opAryElem[ipAryElmKey].push(...ipAryElemVals[ipAryElemKyIdx]);
}
});
if(!isKeyPresent) {
opAry.push({[ipAryElmKey]:ipAryElemVals[ipAryElemKyIdx]});
}
})
});
console.log(opAry);

.filter() same data from different sources [duplicate]

This question already has answers here:
How to compare two array of object and get common objects
(3 answers)
How to determine equality for two JavaScript objects?
(82 answers)
Closed 4 years ago.
I have 2 arrays:
var yin = [{"_id": "11111", "name": "blue"}];
var yang = [{"_id": "11111", "name": "blue"}, {"_id": "22222", "name": "red"}];
I try and filter out the following like so:
var yang = yang.filter(function(e){ return this.indexOf(e) < 0; }, yin);
For some reason, indexOf(e) is returning -1 where I know it is the exact same data. The only thing I can think of is there is some relational (probably not the right term) data that is lying underneath that makes it seem like they are not the same since the objects are coming from completely different database sources.
Is there another method to do filter out the same data out of an array of objects that come from different sources?
I know this filter function works because if I push data using .push() it will filter out just fine.
The problem is that two distinct object will never be equal in the sense you mean. You will need to do a deep compare of the objects (manually) or if it suits your needs you can check for equal _id properties.
var yin = [{"_id": "11111", "name": "blue"}];
var yang = [{"_id": "11111", "name": "blue"}, {"_id": "22222", "name": "red"}];
yang = yang.filter(function(e){
return this.findIndex(function(y){return y._id === e._id}) < 0; }, yin);
console.log(yang);
The problem is that they don't have the same objects.
They have objects that look the same.
As a simple example, check this out:
console.log({} == {});
console.log({} === {});
Notice, even though those objects look exactly the same, they aren't equal. Unlike primitives (string, number), objects are all unique from one another.
What you need to do instead is implement your own contains() function that will do a comparison of each property in the object:
const yin = [{"_id": "11111", "name": "blue"}];
const yang = [{"_id": "11111", "name": "blue"}, {"_id": "22222", "name": "red"}];
function contains(arr, el) {
return arr.some(i => equal(i, el));
}
function equal(a, b) {
return Object.keys(a).length === Object.keys(b).length
&& Object.entries(a).every(([key, val]) => a[key] === b[key]);
}
// or !contains() if you want the difference
const result = yang.filter(function(el) { return contains(this, el) }, yin);
console.log(result);
The equal() function there only does a shallow comparison. If the objects are more complex, you'll want something that'll do a deep equals and recurse through all elements. Luckily, there are lots of options for this function already implemented in many popular libraries and standalone Node modules, so you don't have to implement it yourself.
The problem:
var yin = [{"_id": "11111", "name": "blue"}];
yin.indexOf({"_id": "11111", "name": "blue"}) // return -1
So, I recommend to use a new function for filter the fields you need.
With Arrow Function:
yang.filter( e => yin.some(a => (a._id == e._id && a.name == e.name)));
Without Arrow Function:
yang.filter( function(elementYang) {
return yin.some(function(elementYing) {
return (elementYang._id == elementYing._id) && (elementYang._name == elementYing._name);
});
});
This will work.
var new = yang.filter((yang) => {
for (let i = 0; i < yin.length; i++) {
if (yin[i]._id==yang._id) {
return yang;
}
}
});
You need the sam object reference for comparing objects.
var object = { _id: "11111", name: "blue" },
yin = [object],
yang = [object, { _id: "22222", name: "red" }];
yang = yang.filter(function(e){ return this.indexOf(e) < 0; }, yin);
console.log(yang)
Without object reference by assiming the same keys of the objects.
var yin = [{ _id: "11111", name: "blue" }],
yang = [{ _id: "11111", name: "blue" }, { _id: "22222", name: "red" }];
yang = yang.filter(o => !yin.some(p => Object.keys(o).every(k => o[k] === p[k])));
console.log(yang);

Compare two object with deep comparision or with json.stringify?

I am having a complex JSON object which I want to compare like below :
$scope.new = [
{
"name": "Node-1",
"isParent": true,
"text" : [
{
"str" : "This is my first Node-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1 string",
"parent":[]
}],
"nodes": [
{
"name": "Node-1-1",
"isParent": false,
"text" : [
{
"str" : "This is my first Node-1-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1-1 string",
"parent":[]
}],
"nodes": [
{
"name": "Node-1-1-1",
"isParent": false,
"text" : [
{
"str" : "This is my first Node-1-1-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1-1-1 string",
"parent":[]
}],
"nodes": []
}
]
}
]
}
]
But while comparing I want to ignore 1 property also but as I am using Angular.js I don't see any option in angular.equal which will omit that property while comparing 2 object.
console.log(angular.equals($scope.new,$scope.copy));
So while doing research I came with below answer which is using lodash having emit option but problem is I guess omit create a copy and I guess I will have performance degradation in case of lodash.
Exclude some properties in comparison using isEqual() of lodash
So now I am thinking to convert object so string and then do comparison and I guess that will be fast but problem is how I will omit that property while string comparison?
Something like this:
var str1 = JSON.stringify(JSON.stringify($scope.new));
var str2 = JSON.stringify(JSON.stringify($scope.copy));
console.log(str1==str2);
Note: I want to ignore isParent property while comparing 2 object.
What would be the best way to do compare 2 object?
Converting to strings is not the best approach in these cases.
Keep them as objects.
Using loadash:
const propertiesToExclude = ['isParent'];
let result = _.isEqual(
_.omit(obj1, propertiesToExclude),
_.omit(obj2, propertiesToExclude)
);
Using plain AngularJS, create a copy of the objects removing the not needed properties and then compare them:
let firstObj = angular.copy(obj1);
let secondObj = angular.copy(obj2);
const propertiesToExclude = ['isParent'];
function removeNotComparatedProperties(obj) {
propertiesToExclude.forEach(prop => {
delete obj[prop];
});
}
removeNotComparatedProperties(firstObj);
removeNotComparatedProperties(secondObj);
angular.equals(firstObj, secondObj);
You can use lodash and override the standard comparator used for deep comparison if you use _.isEqualWith:
var objA = {
isParent: true,
foo: {
isParent: false,
bar: "foobar"
}
};
var objB = {
isParent: false,
foo: {
isParent: true,
bar: "foobar"
}
};
var comparator = function(left, right, key) {
if (key === 'isParent') return true; // if the key is 'isParent', mark the values equal
else return undefined; // else fall back to the default behavior
}
var isEqual = _.isEqualWith(objA, objB, comparator);
console.log(isEqual); // true
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
To exclude multiple properties, extend the comparator function accordingly:
var comparator = function(left, right, key) {
if (key === 'isParent' || key === 'anotherKey') return true;
else return undefined;
}
You could also use a number of different approaches syntactically, depending on what you prefer -- a switch statement, an array that you iterate...

how to get unique values from array of objects with addition of numbers

I'm working in wso2 carbon dashboard. My table is containing 2 fields (Name and Number). I Have duplicate name in the objects but with different number. I want unique name with addition of numbers.
[
{
"Name":"Abc",
"number":2
},
{
"Name":"bcd",
"number":3
},
{
"Name":"Abc",
"number":5
}
]
expected output
[
{
"name":"Abc",
"Number":7
},
{
"name":"bcd",
"Number":3
}
]
I'm using java script to achieve such task. please help me
Use Array#reduce method with a reference object.
var data = [{
"Name": "Abc",
"number": 2
}, {
"Name": "bcd",
"number": 3
}, {
"Name": "Abc",
"number": 5
}];
// object for index reference
var ref = {};
// iterate and generate the array
var res = data.reduce(function(arr, o) {
// check index already defined
if (!(o.Name in ref)) {
// if not defined then define index
ref[o.Name] = arr.length;
// and push the element
// you can also use
// arr.push({Name:o.Name, number:o.number});
arr.push(Object.assign({}, o));
} else {
// if index already defined update the number
arr[ref[o.Name]].number += o.number;
}
// return the array reference
return arr;
// set initial value as empty array
}, []);
console.log(res);

Filtering array of Objects Using Lodash or Javascript based on property name

I am Having the Array of objects. Like this
var result=[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"},
{"batchId":455, "licenseId":asfd236645 },
{"batchId":678, "name":"aaa"}]
i want to have the array which is contains all the three properties. the Output should be like this.
[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"}]
can anybody Help me on this
This is simple with the array .filter() method:
var result=[
{"batchId":123, "licenseId":"2345ef34", "name":"xxx"},
{"batchId":345, "licenseId":"2345sdf334", "name":"www"},
{"batchId":145, "licenseId":"234sdf5666", "name":"eee"},
{"batchId":455, "licenseId":"asfd236645" },
{"batchId":678, "name":"aaa"}
];
var filtered = result.filter(function(v) {
return "batchId" in v && "licenseId" in v && "name" in v;
});
console.log(filtered);
The function you pass to .filter() is called for each element in the array. Each element for which you return a truthy value will be included in the resulting array.
In the code above I simply test if all three of those specific properties are present, although there are other tests you could use that would get the same result for that data:
var result=[ {"batchId":123, "licenseId":"2345ef34", "name":"xxx"}, {"batchId":345, "licenseId":"2345sdf334", "name":"www"}, {"batchId":145, "licenseId":"234sdf5666", "name":"eee"}, {"batchId":455, "licenseId":"asfd236645" }, {"batchId":678, "name":"aaa"} ];
var filtered = result.filter(function(v) {
return Object.keys(v).length === 3;
});
console.log(filtered);
Note that you need to put your licenseId values in quotes, because they seem to be string values.
var result = [{
"batchId": 123,
"licenseId": '2345ef34',
"name": "xxx"
}, {
"batchId": 345,
"licenseId": '2345sdf334',
"name": "www"
}, {
"batchId": 145,
"licenseId": '234sdf5666',
"name": "eee"
}, {
"batchId": 455,
"licenseId": 'asfd236645'
}, {
"batchId": 678,
"name": "aaa"
}];
function hasProperties(object) {
return object.hasOwnProperty('batchId') && object.hasOwnProperty('licenseId') && object.hasOwnProperty('name')
}
result.filter(e => hasProperties(e));

Categories

Resources