Map into another and compare node - javascript

I am with some doubts about iterate into a JS Object and some array functions in JavaScript. Let's say I have these variables:
var json1 = "[{"id": 1, "name":"x"}, {"id": 2, "name":"y"}]";
var json2 = "[{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}]";
How can I make a variable with only the IDs in a array
var ids1 = json1.ids (would be 1,2)
var ids2 = json2.ids (would be 1,2,3)
and make another variable only with the IDs that are different
var idsdiff = diff(ids1, ids2) (would be 3)

var json1 = [{"id":1,"name":"x"}, {"id":2,"name":"y"}],
json2 = [{"id":1,"name":"x"}, {"id":2,"name":"y"}, {"id":3,"name":"z"}],
result1 = json1.map(function (a) { return a.id; }),
result2 = json2.map(function (a) { return a.id; });
var diffs = result2.filter(function (item) {
return result1.indexOf(item) < 0;
});
console.log(result1);
console.log(result2);
console.log(diffs);
Note indexOf and filter and map are not available in iE before iE9.
UPDATE: as per #alexandru-Ionutmihai's comment, filter will fail on [1,2,4] and [1,2,3]
This code seems better:
var json1 = [{"id":1,"name":"x"}, {"id":2,"name":"y"}],
json2 = [{"id":1,"name":"x"}, {"id":2,"name":"y"}, {"id":3,"name":"z"}],
result1 = json1.map(function (a) { return a.id; }),
result2 = json2.map(function (a) { return a.id; });
//as per #alexandru-Ionutmihai this is inaccurate for [1,2,4] and [1,2,3]
/*var diffs = result2.filter(function (item) {
return result1.indexOf(item) < 0;
});*/
//here's a workaround
function arr_diff(a, b) {
var i,
la = a.length,
lb = b.length,
res = [];
if (!la)
return b;
else if (!lb)
return a;
for (i = 0; i < la; i++) {
if (b.indexOf(a[i]) === -1)
res.push(a[i]);
}
for (i = 0; i < lb; i++) {
if (a.indexOf(b[i]) === -1) res.push(b[i]);
}
return res;
}
var diffs = arr_diff(result1, result2),
testDiff = arr_diff([1, 2, 4], [1, 2, 3]);
console.log(result1);
console.log(result2);
console.log(diffs);
console.log(testDiff);
arr_diff credit to #Nomaed's comment on this question's answer.

You could use a hash table for the id and make the difference with the value. Then render the result by filtering.
function getId(a) { return a.id; }
var obj1 = JSON.parse('[{"id": 1, "name":"x"}, {"id": 2, "name":"y"}]');
var obj2 = JSON.parse('[{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}]');
var ids1 = obj1.map(getId);
var ids2 = obj2.map(getId);
var hash = {};
ids1.forEach(function (a) {
hash[a] = 1;
});
ids2.forEach(function (a) {
hash[a] = (hash[a] || 0) - 1;
});
var difference = Object.keys(hash).filter(function (a) { return hash[a]; }).map(Number);
console.log(ids1);
console.log(ids2);
console.log(hash);
console.log(difference);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With lodash, you could use _.xor for a symmetric difference.
var ids1 = [1, 2],
ids2 = [1, 2, 3];
console.log(_.xor(ids1, ids2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

If those JSONs are not parsed, you need one extra step before:
json1 = JSON.parse(json1);
If not, please use this code:
var json1 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}];
var json2 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}];
// extra steps, if necessary
// json1 = JSON.parse(json1);
// json2 = JSON.parse(json2);
function returnID (item) {
return item.id;
};
json1 = json1.map(returnID);
json2 = json2.map(returnID);
var diff = json2.filter(function (item) {
return json1.indexOf(item) < 0;
});
console.log(diff);

You can use map method in combination with filter method.
var json1 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}];
var json2 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}];
var j1=json1.map((x)=>{return x.id});
var j2=json2.map((x)=>{return x.id});
var diff = j2.filter(function(el){
return j1.indexOf(el)==-1;
}).concat(j1.filter(function(el){
return j2.indexOf(el)==-1;
}));
console.log(diff);
Also, this code works if both json arrays contains IDs that are different.
var json1 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 4, "name":"y"}, {"id": 5, "name":"y"}];
var json2 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}];
var j1=json1.map((x)=>{return x.id});
var j2=json2.map((x)=>{return x.id});
var diff = j2.filter(function(el){
return j1.indexOf(el)==-1;
}).concat(j1.filter(function(el){
return j2.indexOf(el)==-1;
}));
console.log(diff);

To get the arrays filled with only the id properties of each object, simple do...
var ids1 = json1.map(x => x.id)
var ids2 = json2.map(x => x.id)
If you are using ES6, or a version transpiler, you can use the spread operator to get the difference between the two like:
var diff = [...id1.filter(x => id2.indexOf(x) == -1), ...id2.filter(x => id1.indexOf(x) == -1)]
var json1 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}];
var json2 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}];
var ids1 = json1.map(x => x.id);
var ids2 = json2.map(x => x.id);
var diff = [...ids1.filter(x => ids2.indexOf(x) == -1), ...ids2.filter(x => ids1.indexOf(x) == -1)];
console.log(diff);

Here I let you two functions to get the results that you want:
First function (getIds):
var json1 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}];
var json2 = [{"id": 1, "name":"x"}, {"id": 2, "name":"y"}, {"id": 3, "name":"z"}];
function getIds (array) {
return array.map(function (obj) {
return obj.id;
});
}
console.log(getIds(json1));
console.log(getIds(json2));
Second function (getDiff)
var json1 = [1, 2, 4, 5];
var json2 = [1, 2, 3];
function getDiff (array1, array2) {
return array1.concat(array2).filter(function (id, index, arr) {
return arr.indexOf(id) === arr.lastIndexOf(id);
});
}
console.log(getDiff(json1, json2));

Related

How to get min value of a specific object array element [duplicate]

I have an array of objects and I want to compare those objects on a specific object property. Here's my array:
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
I'd like to zero in on the "cost" specifically and a get a min and maximum value. I realize I can just grab the cost values and push them off into a javascript array and then run the Fast JavaScript Max/Min.
However is there an easier way to do this by bypassing the array step in the middle and going off the objects properties (in this case "Cost") directly?
The reduce is good for stuff like this: to perform aggregate operations (like min, max, avg, etc.) on an array of objects, and return a single result:
myArray.reduce(function(prev, curr) {
return prev.Cost < curr.Cost ? prev : curr;
});
...or you can define that inner function with ES6 function syntax:
(prev, curr) => prev.Cost < curr.Cost ? prev : curr
If you want to be cute you can attach this to array:
Array.prototype.hasMin = function(attrib) {
return (this.length && this.reduce(function(prev, curr){
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}
Now you can just say:
myArray.hasMin('ID') // result: {"ID": 1, "Cost": 200}
myArray.hasMin('Cost') // result: {"ID": 3, "Cost": 50}
myEmptyArray.hasMin('ID') // result: null
Please note that if you intend to use this, it doesn't have full checks for every situation. If you pass in an array of primitive types, it will fail. If you check for a property that doesn't exist, or if not all the objects contain that property, you will get the last element. This version is a little more bulky, but has those checks:
Array.prototype.hasMin = function(attrib) {
const checker = (o, i) => typeof(o) === 'object' && o[i]
return (this.length && this.reduce(function(prev, curr){
const prevOk = checker(prev, attrib);
const currOk = checker(curr, attrib);
if (!prevOk && !currOk) return {};
if (!prevOk) return curr;
if (!currOk) return prev;
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}
One way is to loop through all elements and compare it to the highest/lowest value.
(Creating an array, invoking array methods is overkill for this simple operation).
// There's no real number bigger than plus Infinity
var lowest = Number.POSITIVE_INFINITY;
var highest = Number.NEGATIVE_INFINITY;
var tmp;
for (var i=myArray.length-1; i>=0; i--) {
tmp = myArray[i].Cost;
if (tmp < lowest) lowest = tmp;
if (tmp > highest) highest = tmp;
}
console.log(highest, lowest);
Using Math.min and Math.max:
var myArray = [
{ id: 1, cost: 200},
{ id: 2, cost: 1000},
{ id: 3, cost: 50},
{ id: 4, cost: 500}
]
var min = Math.min(...myArray.map(item => item.cost));
var max = Math.max(...myArray.map(item => item.cost));
console.log("min: " + min);
console.log("max: " + max);
Use sort, if you don't care about the array being modified.
myArray.sort(function (a, b) {
return a.Cost - b.Cost
})
var min = myArray[0],
max = myArray[myArray.length - 1]
Use Math functions and pluck out the values you want with map.
Here is the jsbin:
https://jsbin.com/necosu/1/edit?js,console
var myArray = [{
"ID": 1,
"Cost": 200
}, {
"ID": 2,
"Cost": 1000
}, {
"ID": 3,
"Cost": 50
}, {
"ID": 4,
"Cost": 500
}],
min = Math.min.apply(null, myArray.map(function(item) {
return item.Cost;
})),
max = Math.max.apply(null, myArray.map(function(item) {
return item.Cost;
}));
console.log('min', min);//50
console.log('max', max);//1000
UPDATE:
If you want to use ES6:
var min = Math.min.apply(null, myArray.map(item => item.Cost)),
max = Math.max.apply(null, myArray.map(item => item.Cost));
I think Rob W's answer is really the right one (+1), but just for fun: if you wanted to be "clever", you could do something like this:
var myArray =
[
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
function finder(cmp, arr, attr) {
var val = arr[0][attr];
for(var i=1;i<arr.length;i++) {
val = cmp(val, arr[i][attr])
}
return val;
}
alert(finder(Math.max, myArray, "Cost"));
alert(finder(Math.min, myArray, "Cost"));
or if you had a deeply nested structure, you could get a little more functional and do the following:
var myArray =
[
{"ID": 1, "Cost": { "Wholesale":200, Retail: 250 }},
{"ID": 2, "Cost": { "Wholesale":1000, Retail: 1010 }},
{"ID": 3, "Cost": { "Wholesale":50, Retail: 300 }},
{"ID": 4, "Cost": { "Wholesale":500, Retail: 1050 }}
]
function finder(cmp, arr, getter) {
var val = getter(arr[0]);
for(var i=1;i<arr.length;i++) {
val = cmp(val, getter(arr[i]))
}
return val;
}
alert(finder(Math.max, myArray, function(x) { return x.Cost.Wholesale; }));
alert(finder(Math.min, myArray, function(x) { return x.Cost.Retail; }));
These could easily be curried into more useful/specific forms.
Try (a is array, f is field to compare)
let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);
let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);
// TEST
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
console.log('Max Cost', max(myArray, 'Cost'));
console.log('Min Cost', min(myArray, 'Cost'));
console.log('Max ID', max(myArray, 'ID'));
console.log('Min ID', min(myArray, 'ID'));
for Max
Math.max.apply(Math, myArray.map(a => a.Cost));
for min
Math.min.apply(Math, myArray.map(a => a.Cost));
This can be achieved with lodash's minBy and maxBy functions.
Lodash's minBy and maxBy documentation
_.minBy(array, [iteratee=_.identity])
_.maxBy(array, [iteratee=_.identity])
These methods accept an iteratee which is invoked for each element in
array to generate the criterion by which the value is ranked. The
iteratee is invoked with one argument: (value).
Solution
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
const minimumCostItem = _.minBy(myArray, "Cost");
console.log("Minimum cost item: ", minimumCostItem);
// Getting the maximum using a functional iteratee
const maximumCostItem = _.maxBy(myArray, function(entry) {
return entry["Cost"];
});
console.log("Maximum cost item: ", maximumCostItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Using Array.prototype.reduce(), you can plug in comparator functions to determine the min, max, etc. item in an array.
var items = [
{ name : 'Apple', count : 3 },
{ name : 'Banana', count : 10 },
{ name : 'Orange', count : 2 },
{ name : 'Mango', count : 8 }
];
function findBy(arr, key, comparatorFn) {
return arr.reduce(function(prev, curr, index, arr) {
return comparatorFn.call(arr, prev[key], curr[key]) ? prev : curr;
});
}
function minComp(prev, curr) {
return prev < curr;
}
function maxComp(prev, curr) {
return prev > curr;
}
document.body.innerHTML = 'Min: ' + findBy(items, 'count', minComp).name + '<br />';
document.body.innerHTML += 'Max: ' + findBy(items, 'count', maxComp).name;
For a concise, modern solution, one can perform a reduce operation over the array, keeping track of the current minimum and maximum values, so the array is only iterated over once (which is optimal).
let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);
Demo:
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);
console.log("Min cost:", min);
console.log("Max cost:", max);
This is more better solution
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
var lowestNumber = myArray[0].Cost;
var highestNumber = myArray[0].Cost;
myArray.forEach(function (keyValue, index, myArray) {
if(index > 0) {
if(keyValue.Cost < lowestNumber){
lowestNumber = keyValue.Cost;
}
if(keyValue.Cost > highestNumber) {
highestNumber = keyValue.Cost;
}
}
});
console.log('lowest number' , lowestNumber);
console.log('highest Number' , highestNumber);
Adding onto Tristan Reid's answer (+ using es6), you could create a function that accepts a callback, which will contain the operator you want to be applied to the prev and curr:
const compare = (arr, key, callback) => arr.reduce((prev, curr) =>
(callback(prev[key], curr[key]) ? prev : curr), {})[key];
// remove `[key]` to return the whole object
Then you could simply call it using:
const costMin = compare(myArray, 'Cost', (a, b) => a < b);
const costMax = compare(myArray, 'Cost', (a, b) => a > b);
we can solve problem by two approach
both method is already explained above but the performance test was missing so completing that one
1, native java-script way
2, first sort object then it easy to get min
max from sorted obj
i also test performance of both tow approach
you can also run and test performance... Happy coding (:
//first approach
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
var t1 = performance.now();;
let max=Math.max.apply(Math, myArray.map(i=>i.Cost))
let min=Math.min.apply(Math, myArray.map(i=>i.Cost))
var t2 = performance.now();;
console.log("native fuction took " + (t2 - t1) + " milliseconds.");
console.log("max Val:"+max)
console.log("min Val:"+min)
// Second approach:
function sortFunc (a, b) {
return a.Cost - b.Cost
}
var s1 = performance.now();;
sortedArray=myArray.sort(sortFunc)
var minBySortArray = sortedArray[0],
maxBySortArray = sortedArray[myArray.length - 1]
var s2 = performance.now();;
console.log("sort funciton took " + (s2 - s1) + " milliseconds.");
console.log("max ValBySortArray :"+max)
console.log("min Val BySortArray:"+min)
max = totalAVG.reduce(function (a, b) { return Math.max(a, b)}, -Infinity);
min = totalAVG.reduce(function (a, b) {return Math.min(a, b)}, Infinity);
Another one, similar to Kennebec's answer, but all in one line:
maxsort = myArray.slice(0).sort(function (a, b) { return b.ID - a.ID })[0].ID;
You can use built-in Array object to use Math.max/Math.min instead:
var arr = [1,4,2,6,88,22,344];
var max = Math.max.apply(Math, arr);// return 344
var min = Math.min.apply(Math, arr);// return 1

how to use filter in javascript for an array of arrays object?

Currently I have an array with values:
var array1 = ['new_user', 'promotion']
What i need to do is filter and an object with this array1:
OBJc = [
{"id": 1, "array_": ['promotion', 'test1']},
{"id": 2, "array_": ['test444', 'test1']},
{"id": 3, "array_": ['new_user', 'test1']}
]
I need to filter this json based of if "array_" has any of the values in array1.
So the output would be:
[
{"id": 1, "array_": ['promotion', 'test1']},
{"id": 3, "array_": ['new_user', 'test1']}
]
const filtered = OBJc.filter(obj => obj.array_.some(array1.includes))
Or with es6 destructuring:
const filtered = OBJc.filter({ array_ } => array_.some(array1.includes))
Basically you check each array_ element to see if it's included in array 1, and keep only those who satisfy this condition.
You want to filter, so lets filter.
OBJc.filter()
Now, you want to return true when your property has some value right?
OBJc.filter(value => {
return value['array_'].includes(x)
})
But there are multiple ones, and you need to know if at least some those values are on your list
OBJc.filter(value => {
return array1.some(arrV => value['array_'].includes(arrV));
})
Or if you like one liners:
OBJc.filter(value => array1.some(arrV => value['array_'].includes(arrV)));
Here's the solution:
var array1 = ['new_user', 'promotion']
JSONstr = [
{"id": 1, "array_": ['promotion', 'test1']},
{"id": 2, "array_": ['test444', 'test1']},
{"id": 3, "array_": ['new_user', 'test1']}
]
const result = JSONstr.filter(obj => {
let found = false;
array1.forEach(elm => {
if (obj.array_.includes(elm)) {
found = true;
}
});
if (found) {
return true;
}
});
console.log(result);
Here's one way if you like playing with booleans.
const reduceTruth = (a, b) => a || b;
const matches = JSONstr.filter(element => {
return element.array_.map( x => array1.map(y => y == x).reduce(reduceTruth, false)).reduce(reduceTruth, false);
});

Get specific data from array and put in other array

I have this result in javascript and i want to get data that has value more that 3 and i want to put in other array .
"availableDates": {
"2020-01-24": 1,
"2020-01-23": 3,
"2020-01-22": 2,
"2020-01-21": 1,
"2020-01-25": 4,
"2021-01-07": 1
}
I group here :
const formattedDate = x.reduce((acc,el) => {
const date = el.split(" ")[0];
acc[date] = (acc[date] || 0) + 1;
return acc;
}, {});
now I want to put in other array all that date that has value more than 3 . For example
newarray = [ "2020-01-23", "2020-01-25" ]
Why don't use a simple .filter() over keys of "availableDates":
const grouped = {
"availableDates": {
"2020-01-24": 1,
"2020-01-23": 3,
"2020-01-22": 2,
"2020-01-21": 1,
"2020-01-25": 4,
"2021-01-07": 1
}
};
const newArray = Object.keys(grouped.availableDates).filter((key) => grouped.availableDates[key] >= 3);
console.log(newArray);
You can simply use a for...in loop to iterate over object keys and filter them:
const data = {
"2020-01-24": 1,
"2020-01-23": 3,
"2020-01-22": 2,
"2020-01-21": 1,
"2020-01-25": 4,
"2021-01-07": 1
};
const reducer = (obj, val) => {
const result = [];
for(key in obj) {
if(obj[key] >= val)
result.push(key);
};
return result;
};
console.log(reducer(data, 3));
You could have something like this. I write a complete bunch of the code to make you able to copy/past to test
var availableDates = new Array()
var availableDates = {
"2020-01-24": 1,
"2020-01-23": 3,
"2020-01-22": 2,
"2020-01-21": 1,
"2020-01-25": 4,
"2021-01-07": 1
}
var results = new Array();
for (date in availableDates){
if (availableDates[date] >= 3){
results.push(date)
}
}
console.log(results)

easiest way to transform array into matrix using java-script

I have written down my first JavaScript code to do some dynamic rendering in a webpage:
var c_names = ["Canada", "USA", "israel"]
var c_ids = [1, 2, 3]
var c_domaain = ["www.canada.com", "www.usa.com", "www.israel.com"]
var data_1 = []
var C_data = [
['Country', 'ids', 'Domain']
]
var x = 1
for (i = 0; i == 3; i++) {
var x = x + 1
data_1.push(c_name[x], c_ids[x], c_domain[x])
for (i = 0; i < c_name.length; i++) {
C_data.push(data_1)
}
}
console.log(C_data)
I'm expecting this output:
data = [ ['Country', 'ids', 'Domain'],
['USA', 1, 'www.usa.com'],
['Canada', 2, 'www.usa.com'],
['Israel', 3, 'www.usa.com'],
]
Iterate over one of the arrays and then append the respective items.
var names = ["Canada", "USA", "israel"]
var ids = [1, 2, 3]
var domains = ["www.canada.com", "www.usa.com", "www.israel.com"]
var data = [
["Country", "ID", "Domain"]
]
names.forEach ((name, idx) => {
data.push ([ name, ids [idx], domains [idx]]);
});
console.log(data)
You could take the array in one array and iterate the outer and the the inner array while respecting the index.
var c_names = ["Canada", "USA", "israel"],
c_ids = [1, 2, 3],
c_domaain = ["www.canada.com", "www.usa.com", "www.israel.com"],
c_data = ['Country', 'ids', 'Domain'],
result = [c_names, c_ids, c_data].reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = r[i] || [];
r[i].push(b);
});
return r;
}, []);
result.unshift(c_data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var c_names = ["Canada", "USA", "israel"]
var c_ids = [1, 2, 3]
var c_domaain = ["www.canada.com", "www.usa.com", "www.israel.com"]
var C_data = [
['Country', 'ids', 'Domain']
]
var i = -1;
while ( c_names[++i] ) {
C_data.push( [ c_names[i], c_ids[i], c_domaain[i]] );
}
console.log(C_data)
var c_names = ["Canada","USA","israel" ];
var c_ids = [1,2,3];
var c_domaain = ["www.canada.com","www.usa.com","www.israel.com"];
var data_1 = [];
var C_data = ['Country', 'ids', 'Domain'];
var x = 1;
for(var i = 0; i < c_names.length; i++){
data_1.push(new Array(C_data[i], c_names[i], c_domaain[i]));
};
console.log(data_1);
This is the output of your code which is wrong:
[ [ "Country", "ids","Domain"],
[ "Canada", 1, "Country"],
[ "USA", 2, "ids" ],
[ "israel", 3, "Domain"]
]

Compare JavaScript Array of Objects to Get Min / Max

I have an array of objects and I want to compare those objects on a specific object property. Here's my array:
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
I'd like to zero in on the "cost" specifically and a get a min and maximum value. I realize I can just grab the cost values and push them off into a javascript array and then run the Fast JavaScript Max/Min.
However is there an easier way to do this by bypassing the array step in the middle and going off the objects properties (in this case "Cost") directly?
The reduce is good for stuff like this: to perform aggregate operations (like min, max, avg, etc.) on an array of objects, and return a single result:
myArray.reduce(function(prev, curr) {
return prev.Cost < curr.Cost ? prev : curr;
});
...or you can define that inner function with ES6 function syntax:
(prev, curr) => prev.Cost < curr.Cost ? prev : curr
If you want to be cute you can attach this to array:
Array.prototype.hasMin = function(attrib) {
return (this.length && this.reduce(function(prev, curr){
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}
Now you can just say:
myArray.hasMin('ID') // result: {"ID": 1, "Cost": 200}
myArray.hasMin('Cost') // result: {"ID": 3, "Cost": 50}
myEmptyArray.hasMin('ID') // result: null
Please note that if you intend to use this, it doesn't have full checks for every situation. If you pass in an array of primitive types, it will fail. If you check for a property that doesn't exist, or if not all the objects contain that property, you will get the last element. This version is a little more bulky, but has those checks:
Array.prototype.hasMin = function(attrib) {
const checker = (o, i) => typeof(o) === 'object' && o[i]
return (this.length && this.reduce(function(prev, curr){
const prevOk = checker(prev, attrib);
const currOk = checker(curr, attrib);
if (!prevOk && !currOk) return {};
if (!prevOk) return curr;
if (!currOk) return prev;
return prev[attrib] < curr[attrib] ? prev : curr;
})) || null;
}
One way is to loop through all elements and compare it to the highest/lowest value.
(Creating an array, invoking array methods is overkill for this simple operation).
// There's no real number bigger than plus Infinity
var lowest = Number.POSITIVE_INFINITY;
var highest = Number.NEGATIVE_INFINITY;
var tmp;
for (var i=myArray.length-1; i>=0; i--) {
tmp = myArray[i].Cost;
if (tmp < lowest) lowest = tmp;
if (tmp > highest) highest = tmp;
}
console.log(highest, lowest);
Using Math.min and Math.max:
var myArray = [
{ id: 1, cost: 200},
{ id: 2, cost: 1000},
{ id: 3, cost: 50},
{ id: 4, cost: 500}
]
var min = Math.min(...myArray.map(item => item.cost));
var max = Math.max(...myArray.map(item => item.cost));
console.log("min: " + min);
console.log("max: " + max);
Use sort, if you don't care about the array being modified.
myArray.sort(function (a, b) {
return a.Cost - b.Cost
})
var min = myArray[0],
max = myArray[myArray.length - 1]
Use Math functions and pluck out the values you want with map.
Here is the jsbin:
https://jsbin.com/necosu/1/edit?js,console
var myArray = [{
"ID": 1,
"Cost": 200
}, {
"ID": 2,
"Cost": 1000
}, {
"ID": 3,
"Cost": 50
}, {
"ID": 4,
"Cost": 500
}],
min = Math.min.apply(null, myArray.map(function(item) {
return item.Cost;
})),
max = Math.max.apply(null, myArray.map(function(item) {
return item.Cost;
}));
console.log('min', min);//50
console.log('max', max);//1000
UPDATE:
If you want to use ES6:
var min = Math.min.apply(null, myArray.map(item => item.Cost)),
max = Math.max.apply(null, myArray.map(item => item.Cost));
I think Rob W's answer is really the right one (+1), but just for fun: if you wanted to be "clever", you could do something like this:
var myArray =
[
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
function finder(cmp, arr, attr) {
var val = arr[0][attr];
for(var i=1;i<arr.length;i++) {
val = cmp(val, arr[i][attr])
}
return val;
}
alert(finder(Math.max, myArray, "Cost"));
alert(finder(Math.min, myArray, "Cost"));
or if you had a deeply nested structure, you could get a little more functional and do the following:
var myArray =
[
{"ID": 1, "Cost": { "Wholesale":200, Retail: 250 }},
{"ID": 2, "Cost": { "Wholesale":1000, Retail: 1010 }},
{"ID": 3, "Cost": { "Wholesale":50, Retail: 300 }},
{"ID": 4, "Cost": { "Wholesale":500, Retail: 1050 }}
]
function finder(cmp, arr, getter) {
var val = getter(arr[0]);
for(var i=1;i<arr.length;i++) {
val = cmp(val, getter(arr[i]))
}
return val;
}
alert(finder(Math.max, myArray, function(x) { return x.Cost.Wholesale; }));
alert(finder(Math.min, myArray, function(x) { return x.Cost.Retail; }));
These could easily be curried into more useful/specific forms.
Try (a is array, f is field to compare)
let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);
let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);
// TEST
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
console.log('Max Cost', max(myArray, 'Cost'));
console.log('Min Cost', min(myArray, 'Cost'));
console.log('Max ID', max(myArray, 'ID'));
console.log('Min ID', min(myArray, 'ID'));
for Max
Math.max.apply(Math, myArray.map(a => a.Cost));
for min
Math.min.apply(Math, myArray.map(a => a.Cost));
This can be achieved with lodash's minBy and maxBy functions.
Lodash's minBy and maxBy documentation
_.minBy(array, [iteratee=_.identity])
_.maxBy(array, [iteratee=_.identity])
These methods accept an iteratee which is invoked for each element in
array to generate the criterion by which the value is ranked. The
iteratee is invoked with one argument: (value).
Solution
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
const minimumCostItem = _.minBy(myArray, "Cost");
console.log("Minimum cost item: ", minimumCostItem);
// Getting the maximum using a functional iteratee
const maximumCostItem = _.maxBy(myArray, function(entry) {
return entry["Cost"];
});
console.log("Maximum cost item: ", maximumCostItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Using Array.prototype.reduce(), you can plug in comparator functions to determine the min, max, etc. item in an array.
var items = [
{ name : 'Apple', count : 3 },
{ name : 'Banana', count : 10 },
{ name : 'Orange', count : 2 },
{ name : 'Mango', count : 8 }
];
function findBy(arr, key, comparatorFn) {
return arr.reduce(function(prev, curr, index, arr) {
return comparatorFn.call(arr, prev[key], curr[key]) ? prev : curr;
});
}
function minComp(prev, curr) {
return prev < curr;
}
function maxComp(prev, curr) {
return prev > curr;
}
document.body.innerHTML = 'Min: ' + findBy(items, 'count', minComp).name + '<br />';
document.body.innerHTML += 'Max: ' + findBy(items, 'count', maxComp).name;
For a concise, modern solution, one can perform a reduce operation over the array, keeping track of the current minimum and maximum values, so the array is only iterated over once (which is optimal).
let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);
Demo:
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
[Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);
console.log("Min cost:", min);
console.log("Max cost:", max);
This is more better solution
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
var lowestNumber = myArray[0].Cost;
var highestNumber = myArray[0].Cost;
myArray.forEach(function (keyValue, index, myArray) {
if(index > 0) {
if(keyValue.Cost < lowestNumber){
lowestNumber = keyValue.Cost;
}
if(keyValue.Cost > highestNumber) {
highestNumber = keyValue.Cost;
}
}
});
console.log('lowest number' , lowestNumber);
console.log('highest Number' , highestNumber);
Adding onto Tristan Reid's answer (+ using es6), you could create a function that accepts a callback, which will contain the operator you want to be applied to the prev and curr:
const compare = (arr, key, callback) => arr.reduce((prev, curr) =>
(callback(prev[key], curr[key]) ? prev : curr), {})[key];
// remove `[key]` to return the whole object
Then you could simply call it using:
const costMin = compare(myArray, 'Cost', (a, b) => a < b);
const costMax = compare(myArray, 'Cost', (a, b) => a > b);
we can solve problem by two approach
both method is already explained above but the performance test was missing so completing that one
1, native java-script way
2, first sort object then it easy to get min
max from sorted obj
i also test performance of both tow approach
you can also run and test performance... Happy coding (:
//first approach
var myArray = [
{"ID": 1, "Cost": 200},
{"ID": 2, "Cost": 1000},
{"ID": 3, "Cost": 50},
{"ID": 4, "Cost": 500}
]
var t1 = performance.now();;
let max=Math.max.apply(Math, myArray.map(i=>i.Cost))
let min=Math.min.apply(Math, myArray.map(i=>i.Cost))
var t2 = performance.now();;
console.log("native fuction took " + (t2 - t1) + " milliseconds.");
console.log("max Val:"+max)
console.log("min Val:"+min)
// Second approach:
function sortFunc (a, b) {
return a.Cost - b.Cost
}
var s1 = performance.now();;
sortedArray=myArray.sort(sortFunc)
var minBySortArray = sortedArray[0],
maxBySortArray = sortedArray[myArray.length - 1]
var s2 = performance.now();;
console.log("sort funciton took " + (s2 - s1) + " milliseconds.");
console.log("max ValBySortArray :"+max)
console.log("min Val BySortArray:"+min)
max = totalAVG.reduce(function (a, b) { return Math.max(a, b)}, -Infinity);
min = totalAVG.reduce(function (a, b) {return Math.min(a, b)}, Infinity);
Another one, similar to Kennebec's answer, but all in one line:
maxsort = myArray.slice(0).sort(function (a, b) { return b.ID - a.ID })[0].ID;
You can use built-in Array object to use Math.max/Math.min instead:
var arr = [1,4,2,6,88,22,344];
var max = Math.max.apply(Math, arr);// return 344
var min = Math.min.apply(Math, arr);// return 1

Categories

Resources