Get matched range item from array of objects javascript - javascript

I have one array which is named as tiers and one quantity variable. Now I am trying to get the matched range value from my tier array. Based on the matched result I want to calculate the discount. I tried to with find method but it gives me an unexpected ouput.
Actual output
{id: 1, discount_percent:0, quantity:6, title:"Tier 1"}
Expeceted Output
{id: 3, discount_percent:10, quantity:10, title:"Tier 3"}
let tiers = [
{id: 1, discount_percent:0, quantity:6, title:"Tier 1"},
{id: 2, discount_percent:5, quantity:8, title:"Tier 2"},
{id: 3, discount_percent:10, quantity:10, title:"Tier 3"},
{id: 4, discount_percent:12, quantity:12, title:"Tier 4"},
{id: 5, discount_percent:14, quantity:14, title:"Tier 5"},
{id: 6, discount_percent:20, quantity:16, title:"Tier 6"},
{id: 7, discount_percent:40, quantity:18, title:"Tier 7"},
{id: 8, discount_percent:50, quantity:50, title:"Tier 8"},
]
function calculateDiscount(){
const ordersQuanity = 10;
const tier = tiers.find((_tier) => _tier.quantity <= ordersQuanity);
...
}

For the generic situation of a number of objects with discount_percents and quantitys, .find isn't the right approach because it'll stop as soon as it finds a match. Consider .reduce instead - if an element being iterated over passes the test and it has a greater discount_percent than the current element in the accumulator (if there's anything in the accumulator to begin with), return it instead.
let tiers = [
{id: 1, discount_percent:0, quantity:6, title:"Tier 1"},
{id: 2, discount_percent:5, quantity:8, title:"Tier 2"},
{id: 3, discount_percent:10, quantity:10, title:"Tier 3"},
{id: 4, discount_percent:12, quantity:12, title:"Tier 4"},
]
function calculateDiscount(){
const ordersQuanity = 10;
const bestTier = tiers.reduce((a, tier) => (
tier.quantity <= ordersQuanity && (!a || tier.discount_percent > a.discount_percent)
? tier
: a
), null) || tiers[0]; // alternate with the first element of the array
// if you want to default to that tier even if the quantity isn't sufficient
console.log(bestTier);
}
calculateDiscount();
If you happen to be able to assume that every increased discount_percent comes with a larger quantity, and the array is sorted, you can use .find if you reverse the array first (so that the items with the greatest discount_percent are iterated over first).
let tiers = [
{id: 1, discount_percent:0, quantity:6, title:"Tier 1"},
{id: 2, discount_percent:5, quantity:8, title:"Tier 2"},
{id: 3, discount_percent:10, quantity:10, title:"Tier 3"},
{id: 4, discount_percent:12, quantity:12, title:"Tier 4"},
];
const tiersReversed = [...tiers].reverse();
function calculateDiscount(){
const ordersQuanity = 10;
const tier = tiersReversed
.find((_tier) => _tier.quantity <= ordersQuanity)
|| tiers[0]; // alternate with the first element of the array
// if you want to default to that tier even if the quantity isn't sufficient
console.log(tier);
}
calculateDiscount();
The snippet works just as well for the dataset in the edited question.
let tiers = [
{id: 1, discount_percent:0, quantity:6, title:"Tier 1"},
{id: 2, discount_percent:5, quantity:8, title:"Tier 2"},
{id: 3, discount_percent:10, quantity:10, title:"Tier 3"},
{id: 4, discount_percent:12, quantity:12, title:"Tier 4"},
{id: 5, discount_percent:14, quantity:14, title:"Tier 5"},
{id: 6, discount_percent:20, quantity:16, title:"Tier 6"},
{id: 7, discount_percent:40, quantity:18, title:"Tier 7"},
{id: 8, discount_percent:50, quantity:50, title:"Tier 8"},
]
function calculateDiscount(ordersQuanity){
const bestTier = tiers.reduce((a, tier) => (
tier.quantity <= ordersQuanity && (!a || tier.discount_percent > a.discount_percent)
? tier
: a
), null) || tiers[0]; // alternate with the first element of the array
// if you want to default to that tier even if the quantity isn't sufficient
console.log(bestTier);
}
calculateDiscount(10);
calculateDiscount(20);

`function discountPercent(item){
return item.discount_percent == 10
}
console.log(tiers.find(discountPercent))

Related

Compare two array of objects based on their id's

I am trying to compare two arrays of objects based on their IDs and return a boolean. I have filtered out items from the both arrays based on is_selected === 1. If IDs of both arrays are same will return isModified false or it will return true.
Both of these are an array of checkboxes. The initial array is what I am getting from the backend and the current is modified by the user. The current array length can be changed by changing checkboxes.
With my current approach for loop is not executing from the second time.
function cancel() {
const initialArr = [
{id: 8, name: "Celery", is_selected: 1},
{id: 9, name: "Crustaceans", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
];
const currentArr = [
{id: 8, name: "Celery", is_selected: 1},
{id: 4, name: "Mustard", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
];
let isModified!: boolean;
for (let index = 0; index < currentArr.length; index++) {
const id = currentArr[index].id;
isModified = initialArr.some((o2) => o2.id !== id);
if (isModified) break;
}
if(isModified){
alert('are you sure?');
} else {
console.log('exit the page');
}
}
There's no issue with the for loop in your code. There are few changes that you could do to improve it
Use a map instead of an array for faster lookups. Running an iteration on array everytime is slow, and it will only increase as the size of arr grows.
Check-in each iteration if the value is changed in the currentArr.
You also need to establish what will happen when there is a new item in the currentArr(i.e with new id), will it be counted as modified or as not modified?
Assuming the answer to the above question is yes, the below code should work
function cancel() {
const initialArr = [
{id: 8, name: "Celery", is_selected: 1},
{id: 9, name: "Crustaceans", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
];
const initalArrMap = initialArr.reduce((prev, current) => {
return {...prev, [current.id]: current}
}, {});
const currentArr = [
{id: 8, name: "Celery", is_selected: 1},
{id: 9, name: "Mustard", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
{id: 9, name: "Fish", is_selected: 1},
];
let isModified = currentArr.length !== initialArr.length;
if(!isModified){
for (let index = 0; index < currentArr.length; index++) {
const id = currentArr[index].id;
isModified = initalArrMap[id] ? initalArrMap[id].is_selected != currentArr[index].is_selected : true;
if (isModified) break;
}
}
if(isModified){
alert('are you sure?');
} else {
console.log('exit the page');
}
}
cancel();
Note: I am only checking if is_selected is changed to determine if the currentArr is changed.
Edit: Added a check in case currentArr and initialArr length are not equal
The problem is using initialArr.some((o2) => o2.id !== id).
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false.
What do you want to check? If initialArr does not have currentArr ids, it means my currentArr is modified, so you need to try this one:
!initialArr.some((o2) => o2.id === id)
const initialArr = [
{ id: 8, name: "Celery", is_selected: 1 },
{ id: 9, name: "Crustaceans", is_selected: 1 },
{ id: 2, name: "Eggs", is_selected: 1 },
{ id: 6, name: "Fish", is_selected: 1 }
];
const currentArr = [
{ id: 8, name: "Celery", is_selected: 1 },
{ id: 4, name: "Mustard", is_selected: 1 },
{ id: 2, name: "Eggs", is_selected: 1 },
{ id: 6, name: "Fish", is_selected: 1 }
];
let isModified = false;
for (let index = 0; index < currentArr.length; index++) {
const id = currentArr[index].id;
isModified = !initialArr.some((o2) => o2.id === id);
if (isModified)
break;
}
if (isModified) {
alert('are you sure?');
} else {
console.log('exit the page');
}
It will compare each element of first array with all elements of 2nd array
sorting key will make sure it gives correct result if sequence of keys are changed .
it will break the loop as soon as it finds the first different object.
function cancel() {
const a = [
{id: 8, name: "Celery", is_selected: 1},
{id: 9, name: "Crustaceans", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
];
const b = [
{id: 8, name: "Celery", is_selected: 1},
{id: 4, name: "Mustard", is_selected: 1},
{id: 2, name: "Eggs", is_selected: 1},
{id: 6, name: "Fish", is_selected: 1},
];
let isModified;
let ctr =0;
for (let index = 0; index < a.length; index++) {
for(let j=0; j<b.length; j++){
var aKeys = Object.keys(a[index]).sort();
var bKeys = Object.keys(b[j]).sort();
if(aKeys.toString()!== bKeys.toString()){
ctr++;
break;
}
}
}
if(ctr){
alert('Modified');
} else {
console.log('not modified');
}
}
cancel();
The reason it was returning true for every item in the array is not because of logic error but syntax error, check your array of Objects they don't have commas that separate array elements

Remove equal objects from two arrays

I have the following problem in my ReactJs application. Let's say I have two arrays like this:
var cart = [
{id: 1, name: "item1"},
{id: 2, name: "item2"},
];
var productsArr = [
{proId: 1, category: 'cat1'},
{proId: 5, category: 'cat7'},
];
Is it possible to compare these 2 arrays and find any objects in productsArr which cart's id quals productsArr's proId and remove that object from only productsArr?
(If so, as I explained in this example, productsArr[0] should be removed.)
Thanks in advance.
You can use Array#filter in conjunction with Array#some.
var cart = [
{id: 1, name: "item1"},
{id: 2, name: "item2"},
];
var productsArr = [
{proId: 1, category: 'cat1'},
{proId: 5, category: 'cat7'},
];
productsArr = productsArr.filter(({proId})=>!cart.some(({id})=>proId === id));
console.log(productsArr);
var cart = [
{id: 1, name: "item1"},
{id: 2, name: "item2"},
];
var productsArr = [
{proId: 1, category: 'cat1'},
{proId: 5, category: 'cat7'},
];
for (var i = 0; i<productsArr.length; i++) {
if (cart.find(item => item.id === productsArr[i].proId)) {
productsArr.splice(i,1);
i--;
}
}
console.log(productsArr);

How to print extra object in an array of objects after comparison?

const array1 = [
{id: 1, Q_type: "AL"},
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 4, Q_type: "DL"}
]
const array2 = [
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 4, Q_type: "DL"}
]
const arrAfterComparison = array1.filter(val => !array2.includes(val))
I am trying to compare array1 and array2 and getting the object which is not present in both of these arrays
Expected output
arrAfterComparison = [{id:1,Q_type:"AL"}]
Use Array.some() inside Array.filter() method callback.
const array1 = [
{id: 1, Q_type: "AL"},
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 4, Q_type: "DL"}
]
const array2 = [
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 1, Q_type: "DL"}
]
const output = array1.filter(item => !array2.some(a => a.Q_type === item.Q_type))
console.log(output);
When you compare, actually 2 objects are not similar.. look closely(it isn't an error)
Both the ones with id:1 have different values for Q_type
The one with the id of 4(we all see this one)
const array1 = [
{id: 1, Q_type: "AL"},
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 4, Q_type: "DL"}
]
const array2 = [
{id: 2, Q_type: "BL"},
{id: 3, Q_type: "CL"},
{id: 1, Q_type: "DL"}
]
function oddIndexesOut(_arr1,_arr2){
//objects may "look" the same but if they don't point to the same thing, they're not equal.. however if i turn it into a string, things that "look" equal are equal
_arr1=_arr1.map(a=>JSON.stringify(a))
_arr2=_arr2.map(a=>JSON.stringify(a))
//comparison function(if things "look" similar)
function compare(arr1,arr2){
var x=arr1.filter(a=>!arr2.includes(a))
return x.map(a=>JSON.parse(a))
}
//the longest array is used(so that checking can be full)
if(_arr1.length>_arr2.length){
return compare(_arr1,_arr2)
}
return compare(_arr2,_arr1)
}
console.log(oddIndexesOut(array1,array2))

Javascript concat and overwrite where element has the same id

Is it possible to concat two arrays with objects and let the second array overwrite the first array where they have the same id:
// array 1
[
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
// array 2:
[
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
// out:
[
{id: 1, name: "newFoo"}, // overwriten by array 2
{id: 2, name: "bar"}, // not changed (from array 1)
{id: 3, name: "baz"}, // not changed (from array 1)
{id: 4, name: "y"}, // added (from array 2)
{id: 5, name: "z"} // added (from array 2)
]
If it is possible I would like to do this without the use of third party libraries
var a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var b = [
{id: 1, name: "fooboo"},
{id: 4, name: "bar"},
{id: 5, name: "baz"}
];
/* iterate through each of b, if match found in a, extend with that of a. else push into b ...*/
b.forEach(m => {
var item = a.find(n => n.id === m.id);
if(item) { return Object.assign(item, m); }
a.push(m);
});
console.log(a);
You can do
let arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
let arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
let result = arr1.concat(arr2).reduce((a, b) => {
a[b.id] = b.name;
return a;
},{})
result = Object.keys(result).map(e => {
return {id : e, name : result[e]};
});
console.log(result);
Explanation
I am using the property of objects that they don't keep duplicate keys, so for an array concated together, I reduce it to an object with id as it's key and name as its value, hence overriding all duplicates. In the next step I converted this back into an array.
Check you my solution. There is no "rewrite", i just use a second array as base and don't write value if it has same id.
let a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
let b = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
let duplicateId;
a.forEach(aitem => {
duplicateId = false;
b.forEach(bitem => {
if (aitem.id === bitem.id)
duplicateId = true;
});
if (!duplicateId)
b.push(aitem);
});
console.log(b);
Maybe you can use Object.assign and Object.entries to achieve, lets say:
const arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
const arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
const obj3 = Object.entries(Object.assign({}, ...arr1, arr2))
.map(([prop, value]) => ({[prop]:value}));
Example:
https://jsfiddle.net/0f75vLka/
Another option would be to convert arrays to map with id as key then merge the objects and then convert it back to array.
var arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
function arr2map(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
map[item.id] = item;
}
return map;
}
function map2arr(map) {
var arr = [];
for (var i in map) {
arr.push(map[i]);
}
return arr;
}
var arr1m = arr2map(arr1);
var arr2m = arr2map(arr2);
var arr3m = map2arr( Object.assign({}, arr1m, arr2m) );
//output
alert(JSON.stringify(arr3m));

Set order to array of object in javascript

I need to find a simplest way for setting order to array of objects.
For example, there is an array:
var array = [
{id: 1, name: "Matt"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"},
{id: 4, name: "Bruce"}
];
and I have provided
var order = [1,4,2,3];
which refers to object id property of array items.
Now I need to reorder array so it should be like:
var array = [
{id: 1, name: "Matt"},
{id: 4, name: "Bruce"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"}
]
Use Array#sort method for sorting and inside custom sort function use Array#indexOf method to get index.
var array = [{
id: 1,
name: "Matt"
}, {
id: 2,
name: "Jack"
}, {
id: 3,
name: "Morgan"
}, {
id: 4,
name: "Bruce"
}];
var order = [1, 4, 2, 3];
array.sort(function(a, b) {
// sort based on the index in order array
return order.indexOf(a.id) - order.indexOf(b.id);
})
console.log(array);
You can also use reduce() on [1,4,2,3] array to return object where keys will be elements and values will be index of each element and then sort by that object.
var array = [
{id: 1, name: "Matt"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"},
{id: 4, name: "Bruce"}
];
var s = [1,4,2,3].reduce((r, e, i) => {return r[e] = i, r}, {});
var result = array.sort(function(a, b) {
return s[a.id] - s[b.id];
});
console.log(result)
I guess anything that involves sort can not be more efficient than an O(2n) solution. So i would like to do this job with two reduces as follows;
var arr = [{id: 1, name: "Matt"}, {id: 2, name: "Jack"}, {id: 3, name: "Morgan"}, {id: 4, name: "Bruce"}],
order = [1,4,2,3],
lut = order.reduce((t,e,i) => (t[e] = i,t),{}),
result = arr.reduce((res,obj) => (res[lut[obj.id]] = obj, res) ,[]);
console.log(result);

Categories