How can I compare values from multiple objects? - javascript

I want to compare values from objects that I keep in an array.
I know that I can create new arrays with values from each object but I'm trying to find some way to do it without creating them.
Consider we have such situation:
soldiers[first, second, third]
first{name: John, shooting: 95, combat: 50, tactic: 88}
second{name: Arnold, shooting: 97, combat: 72, tactic: 68}
third{name: William, shooting: 87, combat: 86, tactic: 97}
I'd like to select the best soldier from the provided above - but I can't create one rating (i.e. average).
There will be some conditions that soldier must fill - for example: at least 60 points in combat (no matter if every other property is 100).
So I'm trying to find way to compare multiple properties and return name of just one soldier.
I'll appreciate every tip. Thanks!

I have made you an exmaple with comments. Let me know if this pushes you in the right direction or if you need any other help.
const soldiers = [{
name: "John",
shooting: 95,
combat: 50,
tactic: 88
},
{
name: "Arnold",
shooting: 97,
combat: 72,
tactic: 68
},
{
name: "William",
shooting: 87,
combat: 86,
tactic: 97
}
];
const filteredSoldiers = soldiers
.filter(soldier => soldier.combat > 60) // Find every soldier where combat is higher than 60
.map(soldier => {
return {
name: soldier.name,
average: (soldier.combat + soldier.tactic + soldier.shooting) / 3
};
// map will return an array with the filtered soldiers, and we put their average and their name in there
})
.sort((a, b) => b.average - a.average);
// Lastly we sort them high to low by their average score
console.log(
filteredSoldiers.length > 0 ? filteredSoldiers[0].name : 'No soldiers with combat score higher thn 60'
);
jsfiddle
In the filter condition you can of course add more checks.

You need to got through all items and select best value;
Note that some soldiers can have similar values, that's why values.name is array
let a = {
name: "John", shooting: 95, combat: 50, tactic: 88
};
let b = {
name: "Arnold", shooting: 97, combat: 72, tactic: 68
};
let c = {
name: "William", shooting: 87, combat: 86, tactic: 97
};
let soldiers = [a, b, c];
let values = {
shooting: {
name: [],
score: 0
},
combat: {
name: [],
score: 0
},
tactic: {
name: [],
score: 0
}
};
soldiers.map((item) => {
['shooting', 'combat', 'tactic'].forEach(name => {
if (item[name] > values[name].score) {
values[name].name = [item.name];
values[name].score = item[name];
} else if (item[name] === values[name].score) {
values[name].name.push(item.name);
}
});
});
console.log(values);

Related

How to filter objects based on given condition in JavaScript?

I'm new to this community and just started programming I couldn't find anything about this topic can anyone please solve this.
I need to filter names who's each points in array above 75.
i have tried this and unable iterate over an array.
const candidatesList = [{'name':'Blake Hodges', 'points':[76,98,88,84]},{'name':'James Anderson', 'points':[0,98,12,13]}]
const arrayOfSelecetedCandidates=[]
for(let object of candidatesList){
if (candidatesList.every(candidatesList.points>75)){
arrayOfSelecetedCandidates.push(candidatesList.name)
}
}
console.log(arrayOfSelecetedCandidates);
Maybe you want this?
const candidatesList = [{'name':'Blake Hodges', 'points':[76,98,88,84]},{'name':'James Anderson', 'points':[0,98,12,13]}]
const arrayOfSelecetedCandidates=[]
for(let object of candidatesList){
if (object.points.every(i=>i>75))
arrayOfSelecetedCandidates.push(object.name)
}
console.log(arrayOfSelecetedCandidates);
But as #Dai pointed out filter is always better if you want to test an array and return item that pass the test:
const candidatesList = [{'name':'Blake Hodges', 'points':[76,98,88,84]},{'name':'James Anderson', 'points':[0,98,12,13]}]
const arrayOfSelecetedCandidates=candidatesList.filter(i=>i.points.every(y=>y>75))
console.log(arrayOfSelecetedCandidates)
You can achieve this by using Array.filter() method along with Array.every() to test whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value.
Working Demo :
const candidatesList = [{
'name': 'Blake Hodges',
'points': [76, 98, 88, 84]
}, {
'name': 'James Anderson',
'points': [0, 98, 12, 13]
}];
let res = candidatesList.filter((obj) => {
return obj.points.every((item) => item > 75)
});
console.log(res);
"I need to filter names who's each points in array above 75"
Looking at the content of OP, I believe that's incorrect. It would make more sense if the desired output would be only the objects (aka candidates) that have an average of points at or above 75 not a total of 75 (although not explained very well). So the plan of attack would be:
Get each of the candidates with a for...of loop:
for (const candidate of candidates) {...
Get each of the candidate's points (candidate.points) and get their sums using .reduce() (note: the return goes to a new property called .total). Then take that total and divide by the number of grades found in points array (it's candidate.points.length = 4):
candidate.total = candidate.points.reduce((sum, cur) => sum + cur, 0);
candidate.gpa = Math.floor(candidate.total / candidate.points.length);
Once candidate.gpa is established, .filter() will determine if it is equal to or greater than 75.
return candidates.filter(candidate => candidate.gpa >= 75);
const candidates = [{
'name': 'Blake Hodges',
'points': [76, 98, 88, 84]
}, {
'name': 'James Anderson',
'points': [0, 98, 12, 13]
}, {
'name': 'Zer0 0ne',
'points': [100, 88, 91, 84]
}, {
'name': 'Ronald McDonald',
'points': [72, 51, 8, 89]
}];
const selected = candidates => {
for (const candidate of candidates) {
candidate.total = candidate.points.reduce((sum, cur) => sum + cur, 0);
candidate.gpa = Math.floor(candidate.total / candidate.points.length);
}
return candidates.filter(candidate => candidate.gpa >= 75);
};
console.log(selected(candidates));

javascript array of object add rank but don't change order of the main array

I want to add a rank key on each element based on totalpremium value(but I don't want to change the order of objects inside array), My current code works fine but It changes the order of the main array of object which I don't want to do.
Array of objects:
[
{
totalcount: 2834682,
totalpremium: 652553820
},
{
totalcount: 1380674,
totalpremium: 430713235
},
{
totalcount: 862119,
totalpremium: 165983898
}
]
Working Function
// add rank to each object
function rank_by_key(array, key_name) {
if (key_name) {
array.sort(function (a, b) {
return b[key_name] - a[key_name];
});
var rank = 1;
for (var i = 0; i < array.length; i++) {
if (i > 0 && array[i][key_name] < array[i - 1][key_name]) {
rank++;
}
array[i].rank = rank;
}
}
return array;
}
ranked_result = rank_by_key(array, 'totalpremium');
Expected Output:
[
{
"totalcount": 2834682,
"totalpremium": 110,
"rank": 2
},
{
"totalcount": 1380674,
"totalpremium": 121,
"rank":1
},
{
"totalcount": 862119,
"totalpremium": 100,
"rank": 3
}
]
Current Output:
[
{
"totalcount": 1380674,
"totalpremium": 121,
"rank":1
},
{
"totalcount": 2834682,
"totalpremium": 110,
"rank": 2
},
{
"totalcount": 862119,
"totalpremium": 100,
"rank": 3
}
]
This creates a ranks array which we can then reference in creating the new property
let data = [
{
totalcount: 2834682,
totalpremium: 652553820
},
{
totalcount: 1380674,
totalpremium: 43071324534435
},
{
totalcount: 862119,
totalpremium: 165983898
}
]
let ranks = data.map(e => e.totalpremium).sort((a,b) => b-a)
let ranked = data.map( e=> ({...e, rank: (ranks.indexOf(e.totalpremium)+1)}));
console.log(ranked)
All you need is just a sort by field, nothing else. Following array indicies are your expected ranks.
You can do it as follows (if you need to have rank in your elements explicitly):
sort your array copy (sort does mutate original array, that's why you need to create copy),
then use map (map also does not mutate orginal array).
Code example:
const data = [{totalcount:2834682,totalpremium:652553820},
{totalcount:862119,totalpremium:165983898},{totalcount:1380674,totalpremium:430713235}];
const res = [...data].sort((a, b) => b.totalpremium - a.totalpremium)
.map((e, i) => ({rank: i + 1, ...e}))
console.log("Result: ", res)
console.log("Original data: ", data)
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */
Judging from the code you provided, you seem to want the rank to take equality into account, e.g. [10,20,30,20] would be ranked [3,2,1,2] instead of [4,2,1,3].
The performance bottleneck will be the sorting of your keys, but there's little we can do about it.
Here's what I got, using good old functions and intermediate variables.
Some of the ES6 syntactic sugar comes at a cost, while sets seem to work like a charm. I reverted to old-fashioned methods when they seemed to offer a good readability/performance trade-off, but that's highly debatable, of course.
function rank_according_to (data, key_name)
{
// collect unique key values
let unique_keys = new Set();
for (let i = 0 ; i != data.length ; i++) unique_keys.add(data[i][key_name]);
// sort unique key values
let sorted_keys = [...unique_keys].sort( (a,b) => b-a );
// associate a rank to each value
let rank_of_key = [];
for (let i = 0 ; i != sorted_keys.length ; i++) rank_of_key[sorted_keys[i]] = i+1;
// Option 1: Put the ranks into the original array
//for (let i = 0 ; i != data.length ; i++) data[i].rank = rank_of_key[data[i][key_name]];
// Option 2: put the ranks into a deep copy
return data.map (record => ({ ...record, rank: (rank_of_key[record[key_name]]) }) );
}
I changed your dataset to have more readable numbers and a case of identical key values:
console.log (sample)
Array(4) [ {…}, {…}, {…}, {…} ]
​0: Object { key_one: 123, key_two: 121 }
​1: Object { key_one: 135, key_two: 110 }
​2: Object { key_one: 123, key_two: 151 }
​3: Object { key_one: 100, key_two: 100 }
console.log (rank_according_to (sample, "key_one"));
0: Object { key_one: 123, key_two: 121, rank: 2 }
1: Object { key_one: 135, key_two: 110, rank: 1 }
2: Object { key_one: 123, key_two: 151, rank: 2 }
3: Object { key_one: 100, key_two: 100, rank: 3 }
console.log (rank_according_to (sample, "key_two"));
​0: Object { key_one: 123, key_two: 121, rank: 2 }
​1: Object { key_one: 135, key_two: 110, rank: 3 }
​2: Object { key_one: 123, key_two: 151, rank: 1 }
​3: Object { key_one: 100, key_two: 100, rank: 4 }
Now if you ask me, assuming efficiency is not an issue, you could spare yourself some of the hassle by saving the original indexes in an "original_order" property, so that you could sort the array back into place once you're done generating rankings.
At any rate, I'm not too happy with a design that relies on side effects of shallow copies. It's confusing, much harder to maintain, no more efficient than a lot of other possible solutions, and likely to waste far more time in debugging than the extra cost of designing a sturdier solution from the get-go.
You can first sort the object, loop through the result, get the corresponding object and assign the index to the rank property:
Object.entries(arr[0]).sort(([, a], [, b]) => {
return b.totalpremium - a.totalpremium
}).forEach((e, i) => arr[0][(e[0])].rank = i + 1);
Result:
const arr = [{
"A": {
"totalcount": 2834682,
"totalpremium": 110
},
"B": {
"totalcount": 1380674,
"totalpremium": 121
},
"C": {
"totalcount": 862119,
"totalpremium": 100
}
}]
Object.entries(arr[0]).sort(([,a],[,b])=>{return b.totalpremium-a.totalpremium}).forEach((e,i) => arr[0][(e[0])].rank = i+1);
console.log(arr);

Sort Objects That Meet Threshold

I have an array of objects returned by the database that I am attempting to run a sort() on.
[{
PER_ID: 5511,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '353',
DEAL_COUNT_PRESENTED: 118,
},
{
PER_ID: 5016,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '372',
DEAL_COUNT_PRESENTED: 109,
},{
PER_ID: 4181,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '359',
DEAL_COUNT_PRESENTED: 73,
},
{
PER_ID: 5016,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '372',
DEAL_COUNT_PRESENTED: 109,
},{
PER_ID: 158,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '370',
DEAL_COUNT_PRESENTED: 1112,
}]
I'd like to get the value of PER_ID for the object with the lowest value for DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG where also DEAL_COUNT_PRESENTED >= 10.
The code I have is:
per_recs = per_recs.sort(function (prior, curr) {
return ((curr.DEAL_COUNT_PRESENTED >= 10 && (prior.DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG - curr.DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG > 0) ? 1 : 0));
});
let per_id_with_lowest_deal_time_to_present_mins_biz_avg = per_recs[0].PER_ID;
Simplify your problem and filter out the elements that do not interest you first, like that :
const items = [{
PER_ID: 5511,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '353',
DEAL_COUNT_PRESENTED: 118,
},
{
PER_ID: 5016,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '372',
DEAL_COUNT_PRESENTED: 109,
},{
PER_ID: 4181,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '359',
DEAL_COUNT_PRESENTED: 9,
},
{
PER_ID: 5016,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '372',
DEAL_COUNT_PRESENTED: 109,
},{
PER_ID: 158,
DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG: '370',
DEAL_COUNT_PRESENTED: 1112,
}]
const sortedItems = items
.filter(item => item.DEAL_COUNT_PRESENTED >= 10)
.sort((a, b) => {
return +a.DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG - +b.DEAL_TIME_TO_PRESENT_MINS_BIZ_AVG
})
let foundItem = sortedItems[0].PER_ID;
Here you go !
To get the a small number m (1) from n entries a top-m heap sort is usually more efficient. For 1 it is even shorter as a simple reduce can be used (the accumulator represents the heap):
const lowest = records.reduce((current, entry) =>
entry.count >= 10 && (!current || entry.avg < current.avg) ? entry : current, null);

loop through 2 arrays and array of objects and inserting the elements of array 1 and array 2 into the array of object's certain keys

I have an array of objects and an array. My intention is to loop through both and inserting each element of the array into a certain key into the array of objects.
My intention to dynamically insert the values of each array
Notes for Clarification:
arr1 length = 44
arr2 length = 44
arrOfObj length = 44
my thought process of the operation:
loop through arr1 either by forEach or for
loop through arr2
loop through arrOfObject
insert into arrOfObject[i].labels arr1[i]
insert into arrOfObject[i].values arr2[i]
an example:
Array 1
const arr = [ 76, 72, 69, 66, 66, 66, 65, 65, 64, 64, 64, 63, 61, 61, 61, 61, 61, 61, 60, 59, 59, 59, 58, 58, 57, 57, 56, 56, 56, 55, 54, 54, 53, 52, 52, 51, 51, 50, 50, 49, 49, 49, 47, 47]
Array 2
const arr2 = [ "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9", "Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17","Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24", "Item25","Item26", "Item27", "Item28", "Item29", "Item30", "Item31", "Item32", "Item33", "Item34", "Item35", "Item36", "Item37", "Item38", "Item39", "Item40", "Item41", "Item42", "Item43", "Item44]
Array of object
const arrOfObj = [
{
labels:[],
values: [],
},
{
labels:[],
values: [],
},
{
labels:[],
values: [],
},
]
The desired output is
const arrOfObj = [
{
labels:[arr1[0]],
values: [arr2[0]],
},
{
labels:[arr1[1]],
values: [arr2[1]],
},
]
-outcome
const arrOfObj = [
{
labels:['item1'],
values: [76],
},
{
labels:['item2'],
values: [72],
},
]
If there is a way to do it without nested loops (maybe one loop for values insertion and a separate one for labels), as nested loops decrease the execution performance, then it is preferable unless it is a necessity then it is okay.
As well if you a good source for objects and arrays manipulation above the basics level it will great to share
so far my work can be viewed on
https://codesandbox.io/s/epic-archimedes-8kg6h?eslint=1&expanddevtools=1&fontsize=14&hidenavigation=1&theme=dark
thanks a lot in advance
Reworked the original answer to this function.
It will dynamically do what you intended for x array with x length
function combineArr(target, ...arrays){
// looping through the target array
target.map((ele, superIndex)=>{
// looping through the attributes of the target
Object.keys(ele).map((key, index)=>{
// writing the results
target[superIndex][key] = arrays[index][superIndex]
})
})
return target;
}
console.log(combineArr(arrayOfObj,arr1,arr2))
target is your arrOfObj, and arrays will be arr1, arr2
So what it boils down to is that your arrOfObj shares an index with arr1 and arr2. arrOfObj[i] arr1[i] arr2[i] but since arrOfObj[i] is an object with keys we make it iterable with Object.keys and make the arrays iterable aswell with ...arrays so that keys[i] shares index with arrays[i]
Edit: I was able to reproduce the error that was present in your link. The generation of arrOfObj with the template was broken(don't know why) to fix this and a little bit improved version of the combiner see below
// use const when possible
const arr1 = [];
const arr2 = [];
// was fine as is
// Getting the Keys and values of input in 2 separate arrays
inputs.forEach(x => {
for (const key in x) {
arr1.push(key);
arr2.push(x[key]);
}
});
//template made some problems... don't know why
// using arrOfObj to have template dubplicated the same number of arr1 and arr2
const arrOfObj = inputs.map(() => {
return {
labels: [],
values: []
}
})
// function declaration for combiner
function combineArr(target, ...arrays) {
target.map((ele, superIndex) => {
Object.keys(ele).map((key, index) => {
target[superIndex][key].push(arrays[index][superIndex])
})
})
return target;
}
// calling for new array
console.log(combineArr(arrOfObj, arr1, arr2))

if/else statement in map function?

I am here want to use map function in javascript to loop a type data array,but i get error for these syntax below :
function porti(scores) {
const test = scores.map(pass, fail) => {
if (scores < 75){
test.fail
} else {
test.pass
}
return {pass, fail}
}
}
output must be, if scores < 75 : fail, else : pass
console.log(porti([80, 45, 90, 65, 74, 100, 85, 30]));
// { pass: [ 80, 90, 100, 85 ], fail: [ 45, 65, 74, 30 ] }
console.log(porti([]));
// { pass: [], fail: [] }
I think reduce would be better for this situation. This will allow us to reduce the array to an object of two item arrays.
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = items.reduce((obj, item) => {
item < 75 ? obj.fail.push(item) : obj.pass.push(item)
return obj
}, {pass:[], fail:[]})
console.log(result)
If you wanted to use filter you could...
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = {
pass: items.filter(i => i >= 75),
fail: items.filter(i => i < 75)
}
console.log(result)
And here is how we can do it with forEach...
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = {pass:[], fail:[]}
items.forEach(itm => itm < 75 ? result.fail.push(itm) : result.pass.push(itm))
console.log(result)
You could integrate the check as ternary for getting the key for pushing.
function porti(scores) {
var result = { pass: [], fail: [] },
score;
for (score of scores) {
result[score < 75 ? 'fail': 'pass'].push(score);
}
return result
}
console.log(porti([80, 45, 90, 65, 74, 100, 85, 30]));
console.log(porti([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
As mentioned above .map() should best be saved for when you are looking to return an array by manipulating a previous array. If you don't wish to use a vanilla for loop. You could try this
const testScores = [...someArray of numbers]
function porti(tesScores) {
const result = {
pass: [],
fail: []
}
for (let score of testScores) {
if (score < 75) {
result.fail.push(score)
} else {
result.pass.push(score)
}
return result
}}

Categories

Resources