Find max element and its key in Object - React native - javascript

I First must tell that i browsed the site here to find an answer but i couldn't.
I have an object which holds a key(service Name) and a value(counter), i want to extract from this object only the Max and Min value, and their keys.
an example:
Object {
"Learn JavaScript": 3, //Max
"Learn React": 2, //Ignore
"manicure": 1, //Min
}
and then i would like to create an array of objects which will hold them in desecending order
code i used to display the upper outcome:
const arrayofServices = services; //services => state the holding the services
const servicesCounter = arrayofServices.reduce((counterObj, service) => {
if (counterObj.hasOwnProperty(service)) {
counterObj[service] += 1;
return counterObj;
}
return {
...counterObj,
[service]: 1
};
}, {});
console.log("Service Counter in =>" ,servicesCounter);
any suggestions?

One more try :)
const obj = {"Learn JavaScript": 3, "Learn React": 2, "manicure": 1};
function MinMaxFromObj(obj) {
const min = Math.min(...Object.values(obj));
const max = Math.max(...Object.values(obj));
const minKey = Object.entries(obj).find(([key, value]) =>
value === min ? key : null);
const maxKey = Object.entries(obj).find(([key, value]) =>
value === max ? key : null);
return [Object.fromEntries([minKey]), Object.fromEntries([maxKey])];
}
console.log(MinMaxFromObj(obj));

Here is an approach which can help you to solve your problem:
const minMaxEntry = (object) => {
let minKey;
let minValue = Infinity;
let maxKey;
let maxValue = -Infinity;
for (const key in object) {
if (object[key] < minValue) {
minKey = key;
minValue = object[key];
}
if (object[key] > maxValue) {
maxKey = key;
maxValue = object[key];
}
}
const minEntry = { [minKey]: minValue };
const maxEntry = { [maxKey]: maxValue };
return [minEntry, maxEntry];
};
// --- //
const object = {
"Learn JavaScript": 3,
"Learn React": 2,
"manicure": 1,
};
const [minEntry, maxEntry] = minMaxEntry(object);
console.log('Min Entry =>', minEntry);
// { "manicure": 1 }
console.log('Max Entry =>', maxEntry);
// { "Learn JavaScript": 3 }

Related

Rank Order Voting function returning the wrong output

I am building a function that count Rank Order Ballots and returns the winner. The rules of this are that if a candidate has a clear majority then the candidate wins the election.
If not, we remove all reference to that candidate and whichever ballots the candidate go are assigned to whoever came second
So for example if we have this
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2};
Since C has the least number of votes and noone has a majority, all votes C won are then assigned to B, giving B the majority.
This is what I have written:
function removeLowestVotedCandidate(ballots) {
let lowestVotes = Object.entries(ballots).reduce((largestVal, comparison) =>comparison[1] < largestVal[1] ? comparison
:largestVal)[0]
.split('')[0]
//remove lowest voted candidate from object
const newRankedOrderBallots = JSON.parse(
JSON.stringify(ballots)
.replaceAll(`${lowestVotes}`, '')
.replaceAll(/(^[,\s]+)/g, '')
)
//remove leading commas
return Object.fromEntries(Object.entries(newRankedOrderBallots).map(([key, value]) => [key.replace(/^,+/,''), value]))
}
// console.log(removeLowestVotedCandidate(sample))
function getRankedChoiceWinner(ballots) {
let sum = 0
let winnerFound = false
let stretchWin = 0;
let sumByChar = {};
let winner = []
let updatedBallot = ballots
while(winnerFound === false) {
//count overall votes
for(let votes of Object.values(updatedBallot)){
sum +=votes
}
//calculate what is required for a clear majority
stretchWin = Math.round(sum/2)
//count votes assigned to each candidate
for(const[key, val] of Object.entries(updatedBallot)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + val;
}
console.log('sumByChar is currently', sumByChar)
//check if any candidate has a clear majority
winner = Object.entries(sumByChar)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys)
console.log('winner is currently', winner)
if (winner.length === 1) {
winnerFound = true
} else {
updatedBallot = removeLowestVotedCandidate(updatedBallot)
console.log('we are inside else', updatedBallot)
}
}
return winner
}
However, I seem to be getting the wrong answer, I am getting A as opposed to B. This is what is happening with my console.logs
sumByChar is currently { A: 4, B: 3, C: 2 }
winner is currently []
we are inside else { 'A,B,': 4, 'B,,A': 3, 'B,A': 2 }
sumByChar is currently { A: 8, B: 6, C: 4 }
winner is currently []
we are inside else { 'A,,': 4, A: 2 }
sumByChar is currently { A: 12, B: 9, C: 6 }
winner is currently [ 'A' ]
[ 'A' ]
It seems sumByChar is not reseting to zero and instead
There are 2 issues:
Your sumByChar is created outside the loop and mutated inside the loop. Every time there's a new iteration, you add additional values to it. Create the object inside the loop instead, so you get the sum only for the current ballots, not the cumulative sum for all iterations so far.
Your sum variable is also declared outside the loop, and you're adding to it inside the loop. Declare it inside the loop instead.
Also, the input structure is pretty badly designed for something like this. I'd highly recommend restructuring it to make removing candidates easier - using JSON.stringify and a regex just to remove something is extremely suspicious.
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2
};
function removeLowestVotedCandidate(ballots) {
let lowestVotes = Object.entries(ballots).reduce((largestVal, comparison) => comparison[1] < largestVal[1] ? comparison :
largestVal)[0]
.split('')[0]
//remove lowest voted candidate from object
const newRankedOrderBallots = JSON.parse(
JSON.stringify(ballots)
.replaceAll(`${lowestVotes}`, '')
.replaceAll(/(^[,\s]+)/g, '')
)
//remove leading commas
return Object.fromEntries(Object.entries(newRankedOrderBallots).map(([key, value]) => [key.replace(/^,+/, ''), value]))
}
// console.log(removeLowestVotedCandidate(sample))
function getRankedChoiceWinner(ballots) {
let winnerFound = false
let stretchWin = 0;
let winner = []
let updatedBallot = ballots
while (winnerFound === false) {
let sumByChar = {};
//count overall votes
let sum = 0
for (let votes of Object.values(updatedBallot)) {
sum += votes
}
//calculate what is required for a clear majority
stretchWin = Math.round(sum / 2)
//count votes assigned to each candidate
for (const [key, val] of Object.entries(updatedBallot)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + val;
}
// console.log('sumByChar is currently', sumByChar)
//check if any candidate has a clear majority
winner = Object.entries(sumByChar)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys);
console.log('winner is currently', winner)
if (winner.length === 1) {
winnerFound = true
} else {
updatedBallot = removeLowestVotedCandidate(updatedBallot)
// console.log('we are inside else', updatedBallot)
}
}
return winner
}
console.log(getRankedChoiceWinner(sample));
Or, refactored to look halfway decent IMO:
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2
};
const getRankedChoiceWinner = badBallots => checkOneBallot(restructureBallots(badBallots));
const restructureBallots = badBallots => Object.entries(badBallots)
.map(([candidatesStr, votes]) => [candidatesStr.split(','), votes]);
const checkOneBallot = (ballots) => {
const sumVotes = ballots.reduce((a, b) => a + b[1], 0);
const sumByCandidate = {};
for (const [candidates, voteCount] of ballots) {
const candidate = candidates[0];
sumByCandidate[candidate] = (sumByCandidate[candidate] ?? 0) + voteCount;
}
const winningEntry = Object.entries(sumByCandidate).find(([, val]) => val >= sumVotes / 2);
if (winningEntry) return winningEntry[0][0];
return removeLowestAndRetry(ballots, sumByCandidate);
};
const removeLowestAndRetry = (ballots, sumByCandidate) => {
const lowestVal = Math.min(...Object.values(sumByCandidate));
const lowestCandidateEntry = Object.entries(sumByCandidate).reduce(
(a, entry) => entry[1] < a[1] ? entry : a,
['', Infinity]
);
const lowestCandidate = lowestCandidateEntry[0];
for (const ballot of ballots) {
ballot[0] = ballot[0].filter(candidate => candidate !== lowestCandidate);
}
return checkOneBallot(ballots);
};
console.log(getRankedChoiceWinner(sample));

best way to convert from string to javascript object

I have this string:
periodRows.soccer.on:1,periodRows.soccer.periods:1,periodRows.soccer.prematchPeriods=1,periodRows.soccer.label:1st Half
What is the best way to convert it to this object?
periodRows: {
soccer: {
on: 1,
periods: 1,
prematchPeriods: 1,
label: '1st Half',
}
}
Note that I do not control the string, therefore I can not change it.
Thank you
Functionally, a bit shorter.
const f = (obj, keyPath, value) => {
if (keyPath.length === 0) {
return Number.isNaN(Number(value)) ? value : Number(value);
}
const key = keyPath[0];
if (!obj[key]) {
obj[key] = {};
}
obj[key] = f(obj[key], keyPath.slice(1), value);
return obj;
};
const str = "periodRows.soccer.on:1,periodRows.soccer.periods:1,periodRows.soccer.prematchPeriods=1,periodRows.soccer.label:1st Half";
str.split(",")
.map(token => token.split(/[:=]/))
.map(record => ({keyPath: record[0].split("."), value: record[1]}))
.reduce((obj, item) => f(obj, item.keyPath, item.value), {});
Improved recursive solution
const rec = (tokens, index, target) => {
const prop = tokens[index];
if (tokens.length - 1 === index) {
const [lastProp, value] = prop.split(/[:=]/);
target[lastProp] = value;
return target[lastProp];
}
if (prop && !target[prop]) {
target[prop] = {};
}
return target[prop];
}
"periodRows.soccer.on:1,periodRows.soccer.periods:1,periodRows.soccer.prematchPeriods=1,periodRows.soccer.label:1st Half".split(',').reduce((acc, val) => {
const tokens = val.split('.');
let target = acc;
for (let i = 0; i < tokens.length; i++) {
target = rec(tokens, i, target);
}
return acc;
}, {});
By default JS cannot recognize numbers inside of strings. You have to cast it explicitly. You can update code of rec function with this piece.
if (tokens.length - 1 === index) {
const [lastProp, stringValue] = prop.split(/[:=]/);
const parsedValue = +stringValue;
const value = Number.isNaN(parsedValue) ? stringValue: parsedValue;
target[lastProp] = value;
return target[lastProp];
}
The string format you have above is in bad format, the only way to convert it is by first converting it into a string in json-like format, something like the following (notice a json-like string should always be enclosed by {}):
var periodRows = '{"soccer":{"on":1,"periods":1,"prematchPeriods":1,"label":"1st Half"}}';
Then you'd be able to perform the conversion:
//String to Json
const str = JSON.parse(periodRows);
console.log (str);
//Json to string
var periodRows = {
soccer: {
on: 1,
periods: 1,
prematchPeriods: 1,
label: '1st Half',
}
}
var myStr = JSON.stringify(periodRows);
console.log (myStr);

Javascript Text Statistic Algorithm Improvement

I am trying to solve one algorithm in Javascript where the user requires the input sentence then have to do statistic as the following screenshot
I have done with following code
class TextAnalytics {
getAnalytics(sentence) {
var analyzedResult = {}
var textArray = new Array();
const trimmed = sentence.replace(/\s/g, '').toUpperCase()
for (let i = 0; i < trimmed.length; i++) {
const currentChar = trimmed[i]
if (!analyzedResult[currentChar]) {
analyzedResult[currentChar] = {
count: 1,
prevChar: trimmed[i - 1] ? [trimmed[i - 1]] : [],
nextChar: trimmed[i + 1] ? [trimmed[i + 1]] : [],
index: [i]
}
} else {
analyzedResult[currentChar].count++
trimmed[i - 1] &&
analyzedResult[currentChar].prevChar.push(trimmed[i - 1])
trimmed[i + 1] &&
analyzedResult[currentChar].nextChar.push(trimmed[i + 1])
analyzedResult[currentChar].index.push(i)
}
}
return analyzedResult;
}
getMaxDistance(arr) {
let max = Math.max.apply(null, arr);
let min = Math.min.apply(null, arr);
return max - min;
}
}
var textAnalytics = new TextAnalytics();
console.log(textAnalytics.getAnalytics("its cool and awesome"));
Want to check if there is any other way to solve this problem or any refactoring require
Help will be appreciated.
Thanks
You can write it more elegantly:
class CharStats {
constructor () {
this.prevs = [];
this.nexts = [];
this.indexes = [];
}
add (prev, next, index) {
prev && this.prevs.push(prev);
next && this.nexts.push(next);
this.indexes.push(index);
return this;
}
get count () {
return this.indexes.length;
}
get maxDistance () {
// If the index array is empty, the result will be Infinite.
// But because the algorithm cannot have a situation where
// this class is used without at least one index, this case
// need not be covered.
return Math.max(...this.indexes) - Math.min(...this.indexes);
}
}
const getAnalytics = sentence =>
[...sentence.replace(/\s/g, '').toUpperCase()].reduce((map, cur, i, arr) =>
map.set(cur, (map.get(cur) || new CharStats).add(arr[i - 1], arr[i + 1], i)),
new Map);
console.log(getAnalytics('its cool and awesome'));
1) Convert string to array of chars, remove empty, change to upper case
2) Use reduce, go thru each char and build object 'keys' as Char values to have before, after and index.
3) if Char already exist in object, Append new stats and calculate max-distance.
const getAnalytics = str => {
const caps = Array.from(str.toUpperCase()).filter(x => x.trim());
return caps.reduce((acc, char, i) => {
const prepost = {
before: caps[i-1] || '',
after: caps[i+1] || '',
index: i
};
if (char in acc) {
const chars = [...acc[char].chars, prepost];
const mm = chars.reduce((acc, curr) => ({
max: Math.max(acc.max, curr.index),
min: Math.min(acc.min, curr.index)
}), {max: -Infinity, min: Infinity});
acc[char] = { chars, max_distance: mm.max - mm.min };
} else {
acc[char] = { chars: [prepost], max_distance: 0 };
}
return acc;
}, {});
}
console.log(getAnalytics('its cool and awesome'));

Loop through an object-tree by array

I have a question about walk down an object dynamically by an given array object.
Tried with some static code but this is not flexible in a situation where there are more or less levels
// value = 10
// field = ["data", "input", "level", "0"]
item[field[0]][field[1]][field[2]][field[3]] = value
I have no clue where to start with a function doing this with a for loop. Can anybody give me some advice to get started.
You could reduce the fields and take an object and it's properties. At the end assign the value with the last key.
const
setValue = (object, [...path], value) => {
var last = path.pop();
path.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
},
object = {},
value = 10,
fields = ["data", "input", "level", "0"];
setValue(object, fields, value);
console.log(object);
There's a built-in method on Lodash that does just this - _.set.
_.set(item, field, value)
let item = {
data: {
input: {
level: [5]
}
}
};
const field = ["data", "input", "level", "0"];
const value = 10;
_.set(item, field, value);
console.log(item);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You can use a recursive function to navigate through your object :
var value = 10;
var field = ["data", "input", "level", "0"];
var obj =
{
data:
{
input:
{
level: [42]
}
}
};
function SetValue(field, obj, value, index = 0)
{
var memberName = field[index];
// not at last item ?
if (index < field.length - 1)
{
if (obj.hasOwnProperty(memberName))
{
SetValue(field, obj[memberName], value, index + 1);
}
}
else
{
obj[memberName] = value;
}
}
console.log("Before");
console.log(obj);
console.log("---------------------");
SetValue(field, obj, value);
console.log("After");
console.log(obj);
console.log("---------------------");
I already use a function in production that literally does what you want:
/**
* Replace an item in datasource with specified path with new value.
* Will _create_ an item if the path does not currently exist.
* #param {object} o Datasource
* #param {array} k Array of keys used to access item (usually getPath())
* #param {*} v New value of specified item
*/
const replaceItem = (o, k, v) => k.reduce((r, e, i, a) => {
if (!a[i + 1]) r[e] = v;
else return r[e]
}, o)
const dataSource = {
data: {
input: {
level: [1]
}
}
}
const path = ["data", "input", "level", "0"]
replaceItem(dataSource, path, 5)
console.log(dataSource)
another version, with a twist. It differentiates wether the object it creates should be an Object or an Array
const setValue = (object, path, value) => {
if (!object || !path || !path.length) return;
var key = path[0],
i = 1,
prev = key;
while (i < path.length) {
key = path[i++];
object = object[prev] || (object[prev] = +key === (key >>> 0) ? [] : {});
prev = key;
}
object[key] = value;
};
const object = {};
setValue(object, ["data", "input", "level", "0"], 10);
console.log(object);
You could keep a current variable which represents the current object you're on in your object and then set the value at the end once your loop is complete:
const item = {
"data": {
"input": {
"level": {
0: -1
}
}
}
}
let value = 10;
let field = ["data", "input", "level", "0"];
let current = item[field[0]];
for (let i = 1; i < field.length - 1; i++) {
current = current[field[i]];
}
current[field.pop()] = value;
console.log(item);
The above can also be achieved recursively (use nf === undefined instead of !nf if falsy field values exist):
const item = {
"data": {
"input": {
"level": {
0: -1
}
}
}
}
let value = 10;
let field = ["data", "input", "level", "0"];
const set_val = (val, obj, [f, nf, ...rest]) =>
!nf ? obj[f] = val : set_val(val, obj[f], [nf, ...rest]);
set_val(value, item, field);
console.log(item);
If you wish to build the item object from scratch, you could also do something like so:
const value = 10;
const field = ["data", "input", "level", "0"];
let item = {};
let curr = item;
for (let i = 0; i < field.length - 1; i++) {
curr[field[i]] = {};
curr = curr[field[i]];
}
curr[field.pop()] = value;
console.log(item);
This build can also be done recursively like so:
const value = 10;
const field = ["data", "input", "level", "0"];
let item = {};
let curr = item;
const set_val = (val, [f, nf, ...rest], item = {}) => {
if (!nf) {
item[f] = val;
return item;
} else {
item[f] = set_val(val, [nf, ...rest], item[f]);
return item;
}
}
console.log(set_val(value, field));

Find object having maximum value for the `id` property in an array of objects

In my array of objects, I want to find the object with the highest value for the id property.
Here is my array:
myArray = [
{
'id': '73',
'foo': 'bar'
},
{
'id': '45',
'foo': 'bar'
},
// …
];
Generally, I use $.grep to find values in an array, like this:
var result = $.grep(myArray, function (e) {
return e.id == 73;
});
But in this case I need to provide a specific id value for the object I want to select.
The question states that he wants to find the object with the greatest id, not just the greatest id...
var myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
var max = myArray.reduce(function(prev, current) {
if (+current.id > +prev.id) {
return current;
} else {
return prev;
}
});
// max == {'id':'73','foo':'bar'}
const students = [
{ id: 100, name: 'Abolfazl', family: 'Roshanzamir' },
{ id: 2, name: 'Andy', family: 'Madadian' },
{ id: 1500, name: 'Kouros', family: 'Shahmir' }
]
If you want to find the object with max Id :
const item = students.reduce((prev, current) => (+prev.id > +current.id) ? prev : current)
// it returns { id: 1500, name: 'Kouros', family: 'Shahmir' }
If you want to find the object with min Id :
const item = students.reduce((prev, current) => (+prev.id < +current.id) ? prev : current)
// it returns {id: 2, name: "Andy", family: "Madadian"}
If you wnat to find the max Id :
const max = Math.max.apply(null, students.map(item => item.id));
// it returns 1500
If you want to find the min Id :
const min = Math.min.apply(null, students.map(item => item.id));
// it returns 2
Use the map() method of the array. Using map you can provide a function that iterates over every element in the array. In that function, you can work out the object with the highest id. For example:
myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
var maxid = 0;
myArray.map(function(obj){
if (obj.id > maxid) maxid = obj.id;
});
This will give you the max id of the objects in the array.
Then you can use grep to get the related object:
var maxObj = $.grep(myArray, function(e){ return e.id == maxid; });
Alternatively, if you just want the object with the max id, you can do this:
var maxid = 0;
var maxobj;
myArray.map(function(obj){
if (obj.id > maxid) maxobj = obj;
});
//maxobj stores the object with the max id.
var max = 0;
var myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}]
var maxEle = myArray.map(function(ele){ if(ele.id>max){ max=ele} });
map is a function which iterates through array elements and performs specific operation.
let id = items.reduce((maxId, item) => Math.max(maxId, item.id), 0);
or
let id = Math.max(...items.map(item => item.id).concat(0)); // concat(0) for empty array
// slimmer and sleeker ;)
let id = Math.max(...items.map(item => item.id), 0);
This way is more practical, because in the case of an empty array, it returns 0, unlike
Math.max.apply(null, [].map(item => item.id)) // -Infinity
and if you want to get "autoincrement", you can just add 1 regardless of whether the array is empty or not
// starts at 1 if our array is empty
autoincrement = items.reduce((maxId, item) => Math.max(maxId, item.id), 0) + 1;
UPD: Code with map is shorter but with reduce is faster, which is felt with large arrays
let items = Array(100000).fill()
.map((el, _, arr) => ({id: ~~(Math.random() * arr.length), name: 'Summer'}));
const n = 100;
console.time('reduce test');
for (let i = 1; i < n; ++i) {
let id = items.reduce((maxId, item) => Math.max(maxId, item.id), 0);
}
console.timeEnd('reduce test');
console.time('map test');
for (let i = 1; i < n; ++i) {
let id = Math.max(items.map(item => item.id).concat(0));
}
console.timeEnd('map test');
console.time('map spread test');
for (let i = 1; i < n; ++i) {
let id = Math.max(...items.map(item => item.id), 0);
}
console.timeEnd('map spread test');
reduce test: 163.373046875ms
map test: 1282.745849609375ms
map spread test: 242.4111328125ms
If we create an even larger array, spread map will shutdown
let items = Array(200000).fill()
.map((el, _, arr) => ({id: ~~(Math.random() * arr.length), name: 'Summer'}));
reduce test: 312.43896484375ms
map test: 2941.87109375ms
Uncaught RangeError: Maximum call stack size exceeded
at :15:32
function reduceBy(reducer, acc) {
return function(by, arr) {
return arr[arr.reduce(function(acc, v, i) {
var b = by(v);
return reducer(acc[0], b) ? [b, i] : acc;
}, acc || [by(arr[0]), 0])[1]];
};
}
var maximumBy = reduceBy(function(a,b){return a<b;});
var myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
console.log(maximumBy(function(x){
return parseInt(x.id,10)
}, myArray)); // {'id':'73','foo':'bar'}
a shorten version using reduce()
myArray.reduce((max, cur)=>(max.likes>cur.likes?max:cur))

Categories

Resources