Lodash get items that has the lowest value under unique id - javascript

I have an array of objects that represents a set of tours but there are different prices for the same tour like this:
let tours = [{'id':1, price:200},
{'id':1, price:300},
{'id':3, price:150},
{'id':2, price:110},
{'id':3, price:120},
{'id':2, price:100}]
So, I would like to pick the lowest price available by the tour ID and push it into a new array as unique tour with the lowest price.
So the result would be:
result = [{'id':1, price:200},
{'id':3, price:120},
{'id':2, price:100},]
I tried methods in Lodash like _.minBy()but it returns one from all the array.

Lodash Solution
You can _.groupBy() ids, than _.map() the result, and take the lowest of each group with _.minBy():
const tours = [{"id":1,"price":200, prop: 'prop1' },{"id":1,"price":300, prop: 'prop1'},{"id":3,"price":150},{"id":2,"price":110},{"id":3,"price":120},{"id":2,"price":100}];
const result = _(tours)
.groupBy('id')
.map((group) => _.minBy(group, 'price'))
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
VanillaJS Solution
Reduce the tours to a Map with the ids as key. On each iteration take the group with lowest price. When done, spread the Map.values() back to an array:
const tours = [{"id":1,"price":200, prop: 'prop1' },{"id":1,"price":300, prop: 'prop1'},{"id":3,"price":150},{"id":2,"price":110},{"id":3,"price":120},{"id":2,"price":100}];
const lowest = [...tours.reduce((r, o) => {
const { id, price } = o;
const current = r.get(id);
if(!current || price < current.price) r.set(id, { ...o });
return r;
}, new Map()).values()];
console.log(lowest);

Or you can use simply reduce and keep updating minimum value in the accumulator
tours.reduce( (acc, c) => {
acc[c.id] = acc[c.id] ? Math.min( c.price, acc[c.id] ) : c.price; //update min value in accumulator
return acc; // return accumulator
} ,{}) //initialize accumulator to {}
Demo
let tours = [{
'id': 1,
price: 200
},
{
'id': 1,
price: 300
},
{
'id': 3,
price: 150
},
{
'id': 2,
price: 110
},
{
'id': 3,
price: 120
},
{
'id': 2,
price: 100
}
];
var output = tours.reduce((acc, c) => {
acc[c.id] = acc[c.id] ? Math.min(c.price, acc[c.id]) : c.price;
return acc;
}, {});
console.log(output);

Related

Find the highest number in a json object with javascript and a corresponding index or another value

This javascript find the highest number but i want the corresponding id and index too, or the corresponding id. This is not a duplicate question since i'm looking for the id or the index and not just finding the highest number.
const shots = [{
id: 1,
amount: 2
},
{
id: 2,
amount: 4
},
{
id: 3,
amount: 52
},
{
id: 4,
amount: 36
},
{
id: 5,
amount: 13
},
{
id: 6,
amount: 33
}
];
var highest = shots.reduce((acc, shot) => acc = acc > shot.amount ? acc : shot.amount, 0);
OR
var highest = Math.max.apply(Math, shots.map(function(o) { return o.amount; }))
In this example the highest number is 52 then it mean that the corresponding index is 2.
how to get this index value?
Finally, i need to get the corresponding id.
In real life, i should find the highest bitrate to get the corresponding highest quality video url.
Once you have the highest amount, you can .findIndex to get the object with that amount.
const shots=[{id:1,amount:2},{id:2,amount:4},{id:3,amount:52},{id:4,amount:36},{id:5,amount:13},{id:6,amount:33}];
const highest = Math.max(...shots.map(o => o.amount));
const index = shots.findIndex(o => o.amount === highest);
console.log(index, shots[index].id);
Or if you wanted to do it with just a single iteration
const shots=[{id:1,amount:2},{id:2,amount:4},{id:3,amount:52},{id:4,amount:36},{id:5,amount:13},{id:6,amount:33}];
let index = 0;
let best = -Infinity;
shots.forEach((shot, i) => {
if (shot.amount > best) {
best = shot.amount;
index = i;
}
});
console.log(index, shots[index].id);
If you only want the index to get to the ID, it's a bit easier.
const shots=[{id:1,amount:2},{id:2,amount:4},{id:3,amount:52},{id:4,amount:36},{id:5,amount:13},{id:6,amount:33}];
const best = shots.reduce((a, b) => a.amount > b.amount ? a : b);
console.log(best);

convert an array to an object of key value pairs

if I have an array of strings like:
['person,item,cost,amount',
'John,shoes,200,2']
how could I convert this into an object that resembles:
{
'John':[
{
item:'shoes',
cost:'200',
amount:'2',
totalPriceForItems:'400'
}
If I understand correctly, you may try something like this:
const convert = data => {
const [columnsText, ...items] = data;
const columns = columnsText.split(',');
return items.reduce((acc, text) => {
const { person, ...entries } = Object.fromEntries(text.split(',').map((value, i) => [columns[i], value]));
entries.totalPriceForItems = String(entries.cost * entries.amount);
if(acc[person]) {
acc[person].push(entries);
} else {
acc[person] = [entries];
}
return acc;
}, {});
};
const result = convert([
'person,item,cost,amount',
'John,shoes,200,2',
'Bob,glasses,50,3',
'John,shirts,100,5',
]);
console.log(result);
According to your comment,
I have 8 lines of 'John,shoes,200,2' but with different amounts in the
same array. The 'person,item,cost,amount' is only mentioned once at
the start of the array
What I understand is that you have a csv with headers and multiple rows.
If that is the case, then your data would resemble something like this:
data = [
'person,item,cost,amount',
'John,shoes,200,2',
'Adam,pants,60,1',
'Kelly,skirt,180,2',
'John,skirt,150,3'
]
Then you could consider the following approach, that is generic enough to adapt to different headers, and multiple data rows with repeated keys (person names).
// here, you define a function to transform each row of your data,
// like parsing numeric attributes and calculating the totals
function transform(row) {
row.cost = Number.parseInt(row.cost)
row.amount = Number.parseInt(row.amount)
row.total = row.cost * row.amount
return row
}
// The following logic is generic, and can be used
// to map and aggregate any kind of csv with headers
hdrs = data.shift().split(',').slice(1)
rows = data.map(r => r.split(',')).reduce((acc, [n, ...kvs]) =>
({ ...acc, [n]: [...acc[n] || [], transform(Object.fromEntries(kvs.map((v, i) => [hdrs[i], v])))] }), {})
Output:
{
John: [
{ item: "shoes", cost: 200, amount: 2, total: 400 },
{ item: "skirt", cost: 150, amount: 3, total: 450 }],
Adam: [
{ item: "pants", cost: 60, amount: 1, total: 60 }],
Kelly: [
{ item: "skirt", cost: 180, amount: 2, total: 360 }]
}

How can I sort this array in Discord.js?

I have an array, that looks like this(size changes):
[
{ '385090261019131915': 34 },
{ '746430449240375297': 2 },
{ '810189312175374408': 1 },
{ '830832432680009789': 8 },
{ '850073735272988692': 1 }
]
The first value is the member id, the second how many messages the user has.
How can I sort the array, to get the first 10 members, sorted by their messages send?
The code:
if(command === 'leaderboard'){
const list = []
fs.readdirSync('./db/user/messages').forEach(file => {
const user = JSON.parse(fs.readFileSync(`./db/user/messages/${file}` , 'utf-8'))
userid = file.replace('.json','');
const entry = {[userid] : user.userall}
list.push(entry)
})
}
To sort an array by numbers, you can use the .sort() method with a compare function that subtracts the second value from the first one:
const arr = [34, 2, 1, 8, 1]
const sorted = arr.sort((a, b) => b - a)
console.log({ sorted })
As you're using objects, you should sort by an object key, but you're using the user ID as the key, so you don't know them. You can, however, get the value using the [Object.values()][2] method to get the value(s) and sort by them:
const arr = [
{ '385090261019131915': 34 },
{ '746430449240375297': 2 },
{ '810189312175374408': 1 },
{ '830832432680009789': 8 },
{ '850073735272988692': 1 }
]
const sorted = arr.sort((a, b) => Object.values(b)[0] - Object.values(a)[0])
console.log({ sorted })
Don't forget that Object.values() returns an array so you'll need to compare the first element.
However, instead of using the user ID as the key and the points as the value, I'd use two different keys in the object, one for the ID and one for the score:
const list = [
{ id: '385090261019131915', score: 34 },
{ id: '746430449240375297', score: 2 },
{ id: '810189312175374408', score: 1 },
{ id: '830832432680009789', score: 8 },
{ id: '850073735272988692', score: 1 }
]
const sortedList = list.sort((a, b) => b.score - a.score)
console.log({ sortedList })
And the final code:
if (command === 'leaderboard') {
const list = []
fs.readdirSync('./db/user/messages').forEach((file) => {
const user = JSON.parse(
fs.readFileSync(`./db/user/messages/${file}`, 'utf-8'),
)
const userId = file.replace('.json', '')
list.push({ id: userId, score: user.userall })
});
// sort by score
const sortedList = list.sort((a, b) => b.score - a.score)
}

How do I join an array with an array of objects?

I have an array of objects named Indicators and an Array named Departments
For every indicator I’m supposed to add each department. So if there are 4 departments, there should be 4 indicators with the same index but with different departments. How can I achieve this?
EDIT:
Here is my code
let arrDepartment = []
this.DepartmentIR.forEach(e=>{
arrDepartment.push(e.Department)
})
this.RisksIndicatorsIR.forEach(e=>{
let x = {...e}
x.Departments = arrDepartment
console.log(x)
})
})
const departments = [
"department1",
"department2",
"department3",
"department4"
];
const indicators = [
{ name: "indicators1", id: 95 },
{ name: "indicators2", id: 92 },
{ name: "indicators3", id: 93 },
{ name: "indicators4", id: 94 }
];
// Solution with forEach - all departments as key on each indicator
const newIndicators = [];
indicators.forEach((x) =>
newIndicators.push({ ...x, departments: departments })
);
console.log("Solution with forEach", newIndicators);
// Solution with Reduce - all departments as key on each indicator
const newIndicatorsArr = indicators.reduce((acc, x) => {
acc.push({ ...x, departments: departments });
return acc;
}, []);
console.log("Solution with Reduce", newIndicatorsArr);
// Solution if you want 1 department per indicator
const oneDepartmentPerIndicator = indicators.reduce((acc, x, i) => {
acc.push({ ...x, departments: departments[i] });
return acc;
}, []);
console.log("Solution with 1 department per indicator", newIndicatorsArr);

Grouping array with map method

I want to group array of objects by its key,
The Original form;
data = [
{'id': 1, 'name': 'karthik'},
{'id': 1, 'age': 31},
{'id': 2, 'name': 'ramesh'},
{'id': 2, 'age': 22}
];
To transform in to,
groupedData = [
{'id': 1, 'name': 'karthik', 'age': 31},
{'id': 2, 'name': 'ramesh', 'age': 22}
];
What I tried,
this.data.map(item => item.id)
.filter((item, index, all) => all.indexOf(item) === index);
console.log(this.data);
Use reduce instead of map.
const groupedData = Object.values(this.data.reduce((a, { id, ...r }) => ({ ...a, [id]: { ...(a[id] || { id }), ...r }}), {}));
How this works:
Firstly, using reduce is far easier than any map solution because it allows us to have an accumulator value a.
Then, we extract the id, and the rest of the properties (because we don't know what they're called).
We return a, with the property keyed with the value of id being either the existing property or a new one with id, as well as the rest of the properties.
You can use reduce to create an object ( a table ) for each id.
const groupMap = data.reduce((group, currentData) => {
const id = currentData['id']
group[id] = { ...(group[id] || {}), ...currentData }
return group
} ,{})
what this returns is something like:
{
"1": {
"id": 1,
"name": "karthik",
"age": 31
},
"2": {
"id": 2,
"name": "ramesh",
"age": 22
}
}
group[id] = { ...(group[id] || {}), ...currentData } is basically "if you already saw this id, just merge the previous data with the current data"
then you can get the final result calling Object.values
const groupedData = Object.values(groupMap)
which just get the values of the object created above.
Try this
data = data.reduce((total, current, index) => {
for(let key in current) total[index][key] = current[key]
return total
}, [])
This function will help you)
function mergeobj(arrofobj) {
arrofobj.map((item, index) => {
for (let i = 0; i < arrofobj.length; i++) {
if(i==index)continue;
if (item['id'] == arrofobj[i]['id']) {
item = Object.assign(item, arrofobj[i]);
arrofobj.splice(i, 1);
--i;
}
}
return item;
})
return arrofobj; }

Categories

Resources