Sort function with undefined value - javascript

I have an array of object like below:
var stores = [
{
"name": "store3",
"ditance": 8
},
{
"name": "Store5",
"distance": 7,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isFutureOrderPossible": false
}
}
},
{
"name": "Store1",
"distance": 12,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isOpen": true
}
}
},
{
"name": "store2",
"distance": 13,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isOpen": true
}
}
}
]
Expected Result to be sort based on isopen and distance
[
{
"name": "Store1",
"distance": 12,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isOpen": true
}
}
},
{
"name": "store2",
"distance": 13,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isOpen": true
}
}
},
{
"name": "Store5",
"distance": 7,
"web": {
"validateAttributes": {
"isSelectedDispostionSupported": true,
"isFutureOrderPossible": false
}
}
},
{
"name": "store3",
"ditance": 8
}
]
Need to sort an array based on isOpen and distance .The challenge is some object have web property and some object don't have. Even if web available sometimes isOpen won't their. I have tried the below approch it's not working
const sorter = (a, b) => {
if (a.web) {
if (a.web.validateAttributes) {
if (a.web.validateAttributes.isOpen) {
return 1;
} else if (b.web.validateAttributes.isOpen) {
return -1;
} else {
return 1;
};
} else {
return 1;
}
} else {
return 1;
}
};
stores.sort(sorter);

Try this
const stores = [{"name":"store3","ditance":8},{"name":"Store5","distance":7,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isFutureOrderPossible":false}}},{"name":"Store1","distance":12,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"store2","distance":13,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}}]
const sorter = (a,b)=> {
if(!b.web) {
return -1;
}
else if(!b.web.validateAttributes) {
return -1;
}
else if(!b.web.validateAttributes.isOpen) {
return -1;
}
return (a.distance - b.distance)
}
console.log(stores.sort(sorter))

You can use some optional chaining to turn the isOpen properties into a -1 if truthy or 1 if falsy to establish their position.
If those properties are equal, then fallback to a distance comparison.
// fixed "ditance" to "distance" in "store3"
const stores = [{"name":"store3","distance":8},{"name":"Store5","distance":7,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isFutureOrderPossible":false}}},{"name":"Store1","distance":12,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"store2","distance":13,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}}]
const sorted = [...stores].sort((a, b) =>
(a.web?.validateAttributes?.isOpen ? -1 : 1) -
(b.web?.validateAttributes?.isOpen ? -1 : 1) ||
(a.distance - b.distance))
console.log(sorted)
.as-console-wrapper { max-height: 100% !important; }

I see 2 problems.
typo at the first item "ditance": 8
Your sorting function.
Here is my logic. Firstly, check both are Opening or Closing, then compare distance.
Else just simple compare the state open/close
var stores = [{"name":"store3","distance":8},{"name":"Store5","distance":7,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isFutureOrderPossible":false}}},{"name":"Store1","distance":12,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"store2","distance":13,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}}];
stores.sort((a, b) => {
if (a.web?.validateAttributes?.isOpen && b.web?.validateAttributes?.isOpen || !a.web?.validateAttributes?.isOpen && !b.web?.validateAttributes?.isOpen) {
return a.distance - b.distance;
}
if (a.web?.validateAttributes?.isOpen) {
return -1;
}
return 1;
})
console.log(stores);

Try this.
const stores = [{"name":"store3","ditance":8},{"name":"Store5","distance":7,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isFutureOrderPossible":false}}},{"name":"Store1","distance":12,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"store2","distance":13,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}}]
const sorted = stores.slice();
sorted.sort((a, b) => {
if (!a.web || !b.web) {
return -1;
}
if (!a.web.validateAttributes || !b.web.validateAttributes) {
return -1;
}
const x = a.web.validateAttributes.isOpen || false;
const y = b.web.validateAttributes.isOpen || false;
return (x === y) ? a.distance - b.distance : -1;
})
console.log(sorted)
.as-console-wrapper { max-height: 100% !important; top: 0; }

After fixing some typos here is another alternative.
If not set, default each isOpen to false and store.
If they are the same, sort on distance, otherwise sort on the isOpen.
This uses a combination of the Optional Chaining operator and Nullish Coalescing operator
The Optional Chaining operator will return null if any property along the chain does not exist. So in the case of .web or .isOpen not existing a null is produced.
That is followed by the Nullish Caolescing operator so that if a null is produced as above, the default value is false so that the sort order is based upon that value (true will be placed before false.
let stores = [{"name":"Store1","distance":13,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"store2","distance":12,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isOpen":true}}},{"name":"Store5","distance":8,"web":{"validateAttributes":{"isSelectedDispostionSupported":true,"isFutureOrderPossible":false}}},{"name":"store3","distance":7}];
function comparator(a,b) {
let aResult = a?.web?.validateAttributes?.isOpen ?? false;
let bResult = b?.web?.validateAttributes?.isOpen ?? false;
return (aResult === bResult)? a.distance - b.distance : bResult - aResult;
}
console.log(stores.sort(comparator));

Related

Compare 2 Object and list out Missing key and Change in Value Recursively [duplicate]

This question already has answers here:
How to compare two objects and get key-value pairs of their differences?
(7 answers)
Closed 4 months ago.
want to compare 2 out and miss out missing field and change in value recursively
obj1 = {
"val1": "test",
"stream": {
"key": true,
"value": false
},
"val2": "test",
"val3": "test",
}
obj2 = {
"val1": "test",
"stream": {
"key": false,
"value": false
}
}
I need 2 outputs as below
output1 = {
"val2": "test",
"val3": "test"
}
output2 = {
"stream": {
"key": true
}
}
It is some version of deep comparing objects. I combined both outputs into one function since they both represent a different attribute.
var obj1 = {
"val1": "test",
"stream": {
"key": true,
"value": false
},
"val2": "test",
"val3": "test",
}
var obj2 = {
"val1": "test",
"stream": {
"key": false,
"value": false
}
}
function diffCompare(obj1, obj2) {
var list = [];
function is_obj(obj) {
return typeof obj === 'object' && obj !== null
}
function iterate(obj1, obj2, path) {
path = path || []
Object.keys(obj1).forEach(function(key) {
if (obj1[key] != obj2[key]) {
if (is_obj(obj1[key]) && is_obj(obj2[key])) {
iterate(obj1[key], obj2[key], path.concat(key))
} else {
list.push({
path: path.concat(key),
value: obj1[key]
})
}
}
})
}
iterate(obj1, obj2)
// console.log(list)
// building result object:
var result = list.reduce(function(agg, {path, value}) {
var pointer = agg;
while (path.length) {
var part = path.shift();
pointer[part] = pointer[part] || {}
if (path.length) {
pointer = pointer[part]
} else {
pointer[part] = value;
}
}
return agg;
}, {});
return result;
}
console.log(diffCompare(obj1, obj2))
.as-console-wrapper {
max-height: 100% !important
}

How to Sort Nested Array By Multiple Value in JavaScript

I need to sort nested array while sorting order and the sorting key will be dynamic. I am using the query but it work only on plain text.
Sample Data:
[
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
},
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
}
]
Expected Output:
[
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
},
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
}
]
as per the above example sorting is PRICE_CODE1, modelCode with ascending order. I am using the below query-
function sortByMultipleKey(keys) {
return function(a, b) {
if (keys.length == 0) return 0; // force to equal if keys run out
key = keys[0]; // take out the first key
if (a[key] < b[key]) return -1; // will be 1 if DESC
else if (a[key] > b[key]) return 1; // will be -1 if DESC
else return sortByMultipleKey(keys.slice(1))(a, b);
}
}
arr.sort(sortByMultipleKey(['PRICE_CODE1','modelCode']))
above query is working for plain arrays not for arrays of arrays because in example price is array. How to achieve this?
Warning: This would work for the price array structure you have but not for modelCode as sort key.
const data = [
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
},
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
}
]
function sortData(sortKeys = []) {
data.sort((a,b) => {
for (let sortKey of sortKeys) {
const priceObjA = a.price.find(x => sortKey in x);
const priceObjB = b.price.find(x => sortKey in x);
const priceA = priceObjA && priceObjA[sortKey] || 0;
const priceB = priceObjB && priceObjB[sortKey] || 0;
const result = priceA - priceB;
if (result !== 0) {
return result;
}
}
// fallback for equality
return 0;
})
}
sortData(['PRICE_CODE1']);
console.log(JSON.stringify(data,null,2));
sortData(['PRICE_CODE2']);
console.log(JSON.stringify(data,null,2));
sortData(['PRICE_CODE3']);
console.log(JSON.stringify(data,null,2));
arr.sort(sortByMultipleKey(['PRICE_CODE1','modelCode']))
Your sortByMultipleKey takes a list of keys, each of which describe one key. That can't describe the PRICE_CODE1 field of an object under prices.
You're essentially trying to come up with a syntax to describe arbitrary location in hierarchical data.
Instead of doing that, Use Javascript itself to define how to find the next comparison field! pass functions that can resolve the fields and iterate over those.
Below, I will define 2 functions. The first will extract the first PRICE_CODE1 from the elements of prices.
function(d) {
for (i = 0; i < d.price.length; d++) {
if ("PRICE_CODE1" in d.price[i]) {
return d.price[i].PRICE_CODE1
}
}
return undefined
}
The one for modelCode is simpler:
function(d) {
return d.modelCode
}
I also add a 3rd model with the same PRICE_CODE1 so that modelCode would also be relevant.
function sortByMultiple(field_funcs) {
return function(a, b) {
for (i = 0; i < field_funcs.length; i++) {
fa = field_funcs[i](a)
fb = field_funcs[i](b)
console.log("Comparing " + fa + " and " + fb)
if (fa < fb) return -1;
if (fa > fb) return 1;
if (i + 1 == field_funcs.length) return 0
}
}
}
var data = [{
"modelId": 2,
"modelCode": "model2",
"price": [{
"PRICE_CODE1": 100.01
},
{
"PRICE_CODE2": 200.68
},
{
"PRICE_CODE3": 300.0
}
]
},
{
"modelId": 1,
"modelCode": "model1",
"price": [{
"PRICE_CODE1": 225.01
},
{
"PRICE_CODE2": 247.68
},
{
"PRICE_CODE3": 298.0
}
]
},
{
"modelId": 3,
"modelCode": "model3",
"price": [{
"PRICE_CODE1": 225.01
},
{
"PRICE_CODE2": 247.68
},
{
"PRICE_CODE3": 298.0
}
]
}
]
data.sort(sortByMultiple([
function(d) {
for (i = 0; i < d.price.length; d++) {
if ("PRICE_CODE1" in d.price[i]) {
return d.price[i].PRICE_CODE1
}
}
return undefined
},
function(d) {
return d.modelCode
}
]))
console.log(data)
You can use lodash library:
_.orderBy( data, [c => c.price[0].PRICE_CODE1, "modelCode"], ["asc", "asc"]);

variable loses value outside while loop - Javascript

I'm trying to solve this problem involving linkedlists. Partition a linked list so that values less than the partition come first followed by values equal to or greater than the partition.
Example:
input: 90 -> 30 -> 40 -> 50 -> 100 -> 40
partition: 50
output: 30 -> 40 -> 40 -> 50 -> 100 -> 90
Everything less than the partition(50) comes before all nodes greater than the partition(50).
function partitionLl(node) {
let list = {
"head": {
"data": 90,
"next": {
"data": 30,
"next": {
"data": 40,
"next": {
"data": 50,
"next": {
"data": 100,
"next": {
"data": 40,
"next": {
"data": 15,
"next": {
"data": 90,
"next": {
"data": 200,
"next": {
"data": 90,
"next": {
"data": 10,
"next": {
"data": 90,
"next": null
}
}
}
}
}
}
}
}
}
}
}
}
}
let test;
let current = list.head;
let p1 = {
data: null,
next: null
};
let p2 = current;
while (current !== null) {
if (current.data < node) {
p1 = current;
p1 = p1.next;
} else {
p2 = current;
p2 = p2.next;
}
current = current.next;
}
console.log(p1)
console.log(p2)
}
partitionLl(50)
This is the code I have and the list I am aiming to get is great while in the loop, it has the list that I need. The problem is that I need to attach these lists(p1 and p2) when the loop is completed, but the variables log something completely different and inaccurate when outside the while loop.
The current variable is being tracked both in side and outside the loop. Not sure why this is or what kind of scope loop might have that causes this. How would I access the values out side the loop?
The problem with the OPs program is that it never actually changes the list and also does not build a new list for the result, i.e. no assignment to a data or next property ever happens. Only the "pointers" p1 and p2 are moved around, until the end of the list is reached. The way the assignments are done, either p1, or p2 are bound to be null in the end.
A solution would create copies of the current element and append them to either p1 or p2 by setting the next property.
Like so:
function partitionLl(node) {
let list = {
"head": {
"data": 90,
"next": {
"data": 30,
"next": {
"data": 40,
"next": {
"data": 50,
"next": {
"data": 100,
"next": {
"data": 40,
"next": {
"data": 15,
"next": {
"data": 90,
"next": {
"data": 200,
"next": {
"data": 90,
"next": {
"data": 10,
"next": {
"data": 90,
"next": null
}
}
}
}
}
}
}
}
}
}
}
}
}
let p1 = {
"head": null
}
let t1 = p1.head
let p2 = {
"head": null
}
let t2 = p2.head
let current = list.head
while (current !== null) {
if (current.data < node) {
if(p1.head === null) {
p1.head = {
"data": current.data,
"next": null
}
t1 = p1.head
} else {
t1.next = {
"data": current.data,
"next": null
}
t1 = t1.next
}
} else {
if(p2.head === null) {
p2.head = {
"data": current.data,
"next": null
}
t2 = p2.head
} else {
t2.next = {
"data": current.data,
"next": null
}
t2 = t2.next
}
}
current = current.next;
}
console.log(p1)
console.log(p2)
}
partitionLl(50)
Typically working with linked lists are exercises in using recursion.
I'd build a couple of primitives for working with linked lists and then combine them to produce the result that you want - partition can be built from a filter and concat:
// given a list return a new list that contains only those items than pass the test function
const filter = (list, test) => {
const next = list.next ? filter(list.next, test) : null;
if (test(list.data)) {
return { data: list.data, next };
}
return next;
};
// given two lists return a new list with them concatenated together
const concat = (l1, l2) => {
if (!l1.next) {
return { data: l1.data, next: l2 };
}
return { data: l1.data, next: concat(l1.next, l2) };
};
const partition = (list, value) => {
const smaller = filter(list, d => d < value);
const bigger = filter(list, d => d >= value);
return concat(smaller, bigger);
};
You could take three lists for smaller values: (a) left; (b) for greater or equal values right; and (c) split the right part into first and last, where all values goes into the last list until the pivot value is found, and then all values goes into first.
At the end, combine all lists to a single one, and return a new list.
function partition(list, pivot) {
var leftHead = {},
left = leftHead,
firstHead = {},
lastHead = {},
right = lastHead,
isLast = true,
node = list.head;
while (node) {
if (node.data === pivot && isLast) {
right = firstHead;
isLast = false;
}
if (node.data < pivot) {
left.next = { data: node.data };
left = left.next;
} else {
right.next = { data: node.data };
right = right.next;
}
node = node.next;
}
if (firstHead.next) {
right.next = lastHead.next;
left.next = firstHead.next;
} else {
left.next = lastHead.next;
}
return { head: leftHead.next };
}
var list1 = { head: { data: 90, next: { data: 30, next: { data: 40, next: { data: 50, next: { data: 100, next: { data: 40, next: { data: 15, next: { data: 90, next: { data: 200, next: { data: 90, next: { data: 10, next: { data: 90, next: null } } } } } } } } } } } } },
list2 = { head: { data: 90, next: { data: 30, next: { data: 40, next: { data: 50, next: { data: 100, next: { data: 40 } } } } } } };
console.log(partition(list1, 50));
console.log(partition(list2, 50));
.as-console-wrapper { max-height: 100% !important; top: 0; }

IE Array.sort not sorting with compare function

Here is the code example which is not working properly in IE 11.
Element with id = END3 should be the last one.
Just don't tell me that I need to write sorting manually. It is not a big deal to implement it, but really?!
var list = [{
id: "SP1"
},
{
id: "SP4"
},
{
id: "END3"
},
{
id: "SP2"
}
];
console.log(
list.sort(function(a, b) {
if (a.id === "END3") {
return 1;
}
return 0;
})
);
Your sort comparison function is behaving inconsistently. The function is supposed to return < 0, 0, or > 0, not just 1 or 0. If it's not returning those values, you're giving sort the wrong information to work with, because you tell it any comparison in which a isn't the desired value is equal. It's not guaranteed that END3 will be passed as a at any point, so all comparisons will be "equal", so it's undefined what the result will be really. It's also possible that the inconsistency between SP1, END3 ("equal") and END3, SP1 ("greater") will affect the assumptions of the sorting algorithm.
var list = [{id: "SP1"}, {id: "SP4"}, {id: "END3"}, {id: "SP2"}];
console.log(list.sort(function(a, b) {
if (a.id === 'END3') {
return 1;
} else if (b.id === 'END3') {
return -1;
} else {
return 0;
}
}));
Return -1 instead of 0 in the else block. When the compare method returns 0 it leaves a and b unchanged.
var list = [{
id: "SP1"
},
{
id: "SP4"
},
{
id: "END3"
},
{
id: "SP2"
}
];
console.log(
list.sort(function(a, b) {
if (a.id === "END3") {
return 1;
}
return -1;
})
);
Docs

Loop through JSON object and check if particular object exists

I have a JSON object :
[{"box":1,"parent":[],"child":[{"boxId":2}]},{"box":2,"parent":[{"boxId":1}],"child":[]}]
I have a requirement where in I would like to check whether my JSON object has particular box; if yes then check if it has particular child.
eg: check if box 1 exists
if yes then
check if it has child
if yes then
check if it has child boxId=2
How do I do that in javascript/ jquery?
This is how I tried:
var DependantArr=myJSON;
var $hasDependancy;
DependantArr.map(function (boxes) {
if (boxes.box == 2) {
if (boxes.child.length != 0) {
boxes.child.map(function (child) {
$hasDependancy = true;
return false;
});
}
}
This doesn't seem to work as even after I return false it still continues to go in loop. I would like to break the loop if i find a match.
Any suggestion?
You need to iterate over all arrays, you need.
var array = [{ "box": 1, "parent": [], "child": [{ "boxId": 2 }] }, { "box": 2, "parent": [{ "boxId": 1 }], "child": [] }];
function check() {
var found = false;
array.some(function (a) {
if (a.box === 1) {
Array.isArray(a.child) && a.child.some(function (b) {
if (b.boxId === 2) {
found = true;
return true;
}
});
return true;
}
});
return found;
}
document.write(check());
Another solution features a more generic approach, with a given object, which acts as a pattern for the needed items.
[
{ condition: { box: 1 }, nextKey: 'child' },
{ condition: { boxId: 2 } }
]
var array = [{ "box": 1, "parent": [], "child": [{ "boxId": 2 }] }, { "box": 2, "parent": [{ "boxId": 1 }], "child": [] }];
function check(array, conditions) {
function c(a, index) {
var el = conditions[index],
k = Object.keys(el.condition),
v = el.condition[k],
found = false;
return Array.isArray(a) &&
a.some(function (b) {
if (b[k] === v) {
found = true;
if (conditions.length > index + 1) {
found = c(b[el.nextKey], index + 1);
}
return true;
}
}) &&
found;
}
return c(array, 0);
}
document.write(check(array, [{ condition: { box: 1 }, nextKey: 'child' }, { condition: { boxId: 2 } }])+'<br>');
document.write(check(array, [{ condition: { box: 2 }, nextKey: 'parent' }, { condition: { boxId: 1 } }]) + '<br>');
Create a function that will call the filter on your array and return it. The returned value would be an array containing the found object(s) which match(es) your condition(s).
Demo Snippet:
(check the console)
var json = [{"box":1,"parent":[],"child":[{"boxId":2}]},{"box":2,"parent":[{"boxId":1}],"child":[]}];
function search(id, childId) {
return json.filter(function(obj) {
if ((obj.box == id) && (obj.child) && (obj.child.length > 0)) {
return obj.child.filter(function(child) {
return (child.boxId == childId);
});
}
});
}
console.log(search('1', '2')[0]);
console.log(search('2', '2')[0]);
You can make use of recursion. call the same function recursively to check if the element you need is really exist in the array or not.
var json = [{"box":1,"parent":[],"child":[{"boxId":2}]},{"box":2,"parent":[{"boxId":1}],"child":[]}];
var found = false;
function validateJson(data, box, boxId){
for(key in data){
if((data[key].constructor === Object || data[key].constructor === Array) && (key !== box && data[key] !== 1 || key !== boxId && data[key] !== 2)){
arguments.callee(data[key]); // <---calls the same function again.
} else {
found = true; // true only in the case of if "box":1 and "boxId" : 2
}
}
return found;
}
var result = validateJson(json, "box", "boxId");
document.body.innerHTML = '<pre> found : '+JSON.stringify(result) + '</pre>';
Try this
data.forEach(function (el) {
Object.keys(el).forEach(function (property) {
if (el[property] === 'your value to check') {
// do whatever you need here
}
});
});
If this is the only case you need to check, you can use this:
var DependantArr = [{"box": 1, "parent": [], "child": [{"boxId": 3}]}, {"box": 2, "parent": [{"boxId": 1}], "child": []}];
var $hasDependancy = DependantArr.some(function(thisBox) {
if ((thisBox.box === 1) && (thisBox.hasOwnProperty('child'))) {
return thisBox.child.filter(function(thisChild) {
return thisChild.boxId === 2;
}).length > 0;
}
});
I think these will help.
for(var i=DependantArr.length;i>=0;i--) {
return DependantArr[i].box == 1 && DependantArr[i].child.length!=0 &&
DependantArr[i].child[0].boxId==2;
}

Categories

Resources