ES6 Map Reduce array flatten with mapping and padding from Redux state - javascript

I am writing a React/Redux app using ES6 and I want an efficient way to map this data:
[
{total: 50, label: "C1"},
{total: 120, label: "C2"},
{total: 220, label: "C4"}
]
To something like below:
[
{50, "Category 1"},
{120, "Category 2"},
{0, "Category 3"},
{220, "Category 4"}
{0, "Category 5"},
]
The key being that it flattens the original array and relabels the label values and also pads out the missing keys.
I can do this with some ugly looking JS iterating over it but I'm struggling to get this to work with a simple and elegant reduce arrow function mechanism which I'm sure is possible.

Use .map().
var foo = [{
total: 50,
label: "C1"
},
{
total: 120,
label: "C2"
},
{
total: 220,
label: "C4"
}
];
var flatFoo = foo.map((obj) => {
return [
obj.total,
`${obj.label.substr(0, 1)}ategory ${obj.label.substr(1, 1)}`
];
});
console.log(flatFoo);

Use Array.prototype.reduce to create a hashtable to pad out the missing keys to an array of arrays - see demo below:
var arr = [{total: 50, label: "C1"},{total: 120, label: "C2"},{total: 220, label: "C4"}];
// create a hash table first
var hash = arr.reduce(function(p,c){
var key = c.label.replace(/[^\d]/g,'');
p[key] = c;
// largest key
p['count'] = (+key > (p['count'] || 0)) ? +key : p['count'];
return p;
}, Object.create(null));
// accumulate the result
var result = [];
for(var i = 0; i < hash['count']; i++) {
var el = hash[i + 1] || {};
result.push([el.total || 0, 'Category '+ (i+1)]);
}
console.log(result);
.as-console-wrapper {top: 0;max-height: 100%!important;}

Here are some options...
const data = [
{total: 50, label: "C1"},
{total: 120, label: "C2"},
{total: 220, label: "C4"}
];
const stripChars = s => (s || '').replace(/[a-z]/i, '');
function fillTo(limit, data) {
return Array
.from({length: limit}, (_, i) => (
data.find(o => stripChars(o.label) == i) ||
{ total: 0, label: `C${i}` }
))
;
}
const toMap = data => data.reduce(
(res, $1) => Object.assign(res, {
[$1.total]: `Category ${stripChars($1.label)}`
}), Object.create(null)
);
const pairwise = data => data.map(
$1 => [$1.total, `Category ${stripChars($1.label)}`]
);
console.log('fillTo', fillTo(6, data));
console.log('toMap', toMap(data));
console.log('pairwise', pairwise(data));
/**
* 1. fill the array
* 2. chose your transformer
*
* const d = fillTo(6, data);
* console.log(pairwise(d));
**/

You can first sort original array to get min and max value and then use loops to add missing elements.
var data = [
{total: 220, label: "C14"},
{total: 50, label: "C3"},
{total: 120, label: "C10"},
{total: 220, label: "C7"}
]
data.sort((a, b) => a.label.slice(1) - b.label.slice(1))
var result = [];
data.forEach(function(e, i) {
var n = e.label.slice(1);
var next = data[i + 1]
result.push([e.total, 'Category ' + n]);
if (next) {
for (var j = n; j < +(next.label.slice(1) - 1); j++) {
result.push([0, 'Category ' + (+j + 1)])
}
}
})
console.log(JSON.stringify(result, 0, 4))

Related

best way to search and delete with given values, for delete from array inside the object and return new array of object javascript?

I have tried a couple of methods using findIndex, map, Object.entires
someone help me find the best solution?
**
remove 2 from customerNumber array [1,2,3,4,5]
set to customerNumber value with array[1,3,4,5]
and spread customerNumber to filterState array
**
let filterState = [
{'customerNumber': [1,2,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
search and delete 2 from customerNumber//customerNumber : 2
function removedChip(type='', value=0, filterState=[]) {
for(let i=0; i<filterState.length; i++) {
let entries = Object.keys(filterState)
.forEach(function eachKey(key) {
console.log(key); // alerts key
console.log(filterState[key]); // alerts value
});
console.log(entries)
let searchIndex = entries.findIndex(type);
console.log('searchIndex', searchIndex)
console.log('type of ', filterState[searchIndex])
for(let i=0; i<filterState[searchIndex]; i++) {
//remove 2 from customerNumber array [1,2,3,4,5]
// set to customerNumber value with array[1,3,4,5]
// and spread customerNumber to filterState array
}
}
}
function invoking with values
removedChip('customerNumber', 10, filterState)
the expected output is
let filterState = [
{'customerNumber': [1,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
This might help :
function removedChip(type='', value=0, filterState=[]) {
const filterStateTypeArray = filterState.filter(fs =>
Object.keys(fs)[0] === type);
const filterStateTypeItem = filterStateTypeArray ?
filterStateTypeArray[0] : null;
if(!filterStateTypeItem){return;}
let valueArray = filterStateTypeItem[type];
valueArray = valueArray.filter(vA => vA !== value);
filterStateTypeItem[type] = valueArray;
console.log(filterState);
}
let filterState = [
{'customerNumber': [1,2,3,4,5]},
{'ward': [10, 20, 30, 40, 50]},
{'enrolledDate': [111, 222,333, 444,555]},
{'district': ['AAA', 'BBB','CCCC', 'DDD']},
{'onBoardingSmsStatus': false}
]
removedChip('customerNumber', 2, filterState);
Not much of a change from other answers which are all feasible - I'd just split out the functions in 2 to have the filtering handled for an array which can then be tested independently of the parent function or independently from whatever list of objects is inputted
I.e you can have a generic filtering method that can be tested in isolation from the input list of objects.
let filterState = [
{ customerNumber: [1, 2, 3, 4, 5] },
{ ward: [10, 20, 30, 40, 50] },
{ enrolledDate: [111, 222, 333, 444, 555] },
{ district: ['AAA', 'BBB', 'CCCC', 'DDD'] },
{ onBoardingSmsStatus: false },
];
// Independent testable filtering
const removeChipFromArray = (array, removeValue = 0) => {
return array.filter(e => e !== removeValue);
};
// Function to handle the removal of any value from any list of objects
const removeChips = (listOfObjects, value) => {
listOfObjects.forEach((element, index) => {
const key = Object.keys(element);
// General type checker (which would be easier in TS than JS but here is a half-safe way of ensuring you're operating against a list
// You could also convert it to an Array if you think that's better
if (typeof(element[key]) === 'object') {
element[key] = removeChipFromArray(element[key], value);
}
});
};
removeChips(filterState, 2);
console.log(filterState);
In your removedChip You can filter it like..
function removedChip(type = "", value = 0, filterState = []) {
const result = filterState.map((data) => {
if (data[type]) {
// Modify only the given field in the type params
return { [type]: data[type].filter((du) => du !== value) };
}
return data;
});
return result;
}
let filterState = [
{ customerNumber: [1, 2, 3, 4, 5] },
{ ward: [10, 20, 30, 40, 50] },
{ enrolledDate: [111, 222, 333, 444, 555] },
{ district: ["AAA", "BBB", "CCCC", "DDD"] },
{ onBoardingSmsStatus: false }
];
console.log(removedChip("customerNumber", 2, filterState));

How to sum the array of object values and assigned them to the relevant key name

I need to understand the simplest way of doing this.
I've got an array of objects:
const data = [
{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
]
What I'm trying to get is simple object where its key is the month from data.incomes and the value is sum of relative month values, so the final result looks like:
const totalIncomes = {
"2019-12": 125,
"2020-12": 250,
"2021-12": 15
}
Can anybody explain it to me step by step, please?
solved using reduce and forEach
Inside the reduce function I'm running a forEach on the array of keys of the incomes object/attribute. For each key which is a date I'm checking if the accumulator of the reduce function contains an attribute for each date and creates if not. After creating the attribute I'm summing the value for the current date attribute.
const data = [{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
]
const totalIncomes = data.reduce((acc, curr) => {
Object.keys(curr.incomes).forEach((key, index) => {
if (!acc[key]) {
acc[key] = 0
}
acc[key] += curr.incomes[key]
})
return acc
}, {})
console.log(totalIncomes)
Maybe this is not the pretties solutions but you can do it like this, the function is of course not necessary.
const data = [
{
group: "A",
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15,
},
},
{
group: "B",
incomes: {
"2019-12": 25,
"2020-12": 50,
},
},
];
getterInformation(data);
function getterInformation(object) {
let objectWithCalculatedValues = {};
object.forEach((items) => {
for (const key in items.incomes) {
if (objectWithCalculatedValues[key] === undefined) {
objectWithCalculatedValues[key] = 0;
}
objectWithCalculatedValues[key] += items.incomes[key];
}
});
console.log(objectWithCalculatedValues);
}
Assuming that this information may be useful to readers who may be unable to obtain necessary guidance (due to various possible reasons), here is one possible way to achieve the objective (solution):
const aggregateIncomesByMonth = () => (
data.map(d => Object.entries(d.incomes).map(([k, v]) => ({
key: k,
value: v
}))).flat().reduce((fin, itm) => ({
...fin,
[itm.key]: (fin[itm.key] || 0) + itm.value
}), {})
);
Explanation
Extract only the incomes from the data array
For each income object, get the key-value pair and transform into another object of the structure {key: 20yy-mm, value: nn}
Use .flat() to transform the result from step-2 into a 1-dimensional array
Use .reduce to sum the value for those cases where the key (ie, 20yy-mm) matches.
Code-snippet
const data = [{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
];
const aggregateIncomesByMonth = () => (
data.map(d => Object.entries(d.incomes).map(([k, v]) => ({
key: k,
value: v
}))).flat().reduce((fin, itm) => ({
...fin,
[itm.key]: (fin[itm.key] || 0) + itm.value
}), {})
);
console.log(aggregateIncomesByMonth());
My approach here is to destructure the array. This way I have all the data of the incomes of group A in the variable A and the same for B.
Then I do a double loop to compare both objects data and see if the dates match. If so, sum the incomes and add the data to the total object.
const data = [
{
group: 'A',
incomes: { "2019-12": 100, "2020-12": 200, "2021-12": 15 }
},
{
group: 'B',
incomes: { "2019-12": 25, "2020-12": 50 }
}
]
let A, B, total = {};
[A, B] = [data[0].incomes, data[1].incomes]
for(const date in A){
for(const d in B){
total[date] = date === d ? A[date] + B[date] : A[date]
}
}
console.log(total)

Change the structure of my array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'd like to change the structure of my result.There are objects "itemGroup" and I'd like to delete them and keep keys "fruit" and "vegetable".
[{"id": 1, "shop": "shop1", "itemGroup": {"fruit": 2, "vegetable": 2},"total":4},
{"id": 2, "shop": "shop2", "itemGroup": {"fruit": 0, "vegetable": 1},"total":1}]
I'd like this result
[
{ "id": 1, "shop": "shop1", "fruit": 2, "vegetable": 2, "total": 4 },
{ "id": 2, "shop": "shop2" "fruit": 0, "vegetable": 1, "total": 1 }
]
my code
var myArray = [
{shop: "shop1",item1: "my apple 1",item2: "my carrot 1"},
{shop: "shop1",item1: "my apple 1",item2: "my carrot 1"},
{shop: "shop2",item1: "my apple 0",item2: "my carrot 0"},
{shop: "shop2",item1: "my apple 0",item2: "my carrot 1"}
];
var MyArrayDefinition = [
{item: "my apple 0",color: "red", group: "fruit",score: 0},
{item: "my carrot 1",color: "orange",group: "vegetable",score: 1},
{item: "my apple 1",color: "red", group: "fruit",score: 1},
{item: "my carrot 0",color: "orange",group: "vegetable",score: 0}
];
var k = Object.keys,
items = MyArrayDefinition.reduce((o, v) => (o[v.item] = v, o), {});
var shops = myArray.reduce((o, v, i, s) => (
s = v[k(v).find(k => k)],
s = o[s] || (o[s] = {
fruit: 0,
vegetable: 0,
}),
k(v).forEach(k => k.includes('item') &&
(s[(i = items[v[k]]).group] += i.score)), o), {});
var result = k(shops).map((k, i) => ({
id: i + 1,
shop: k,
itemGroup: shops[k],
total:Object.values(shops[k]).reduce((a, b) => a + b),
}));
Pretty much like in most of your questions from the last couple of days. :-)
You can map over the data, use Object.assign and delete the itemGroup.
let x = f.map(e => {
e = Object.assign(e, e.itemGroup);
delete e.itemGroup;
return e;
})
console.log(x);
<script>
let f = [{
"id": 1,
"shop": "shop1",
"itemGroup": {
"fruit": 2,
"vegetable": 2
},
"total": 4
},
{
"id": 2,
"shop": "shop2",
"itemGroup": {
"fruit": 0,
"vegetable": 1
},
"total": 1
}
]
</script>
Expounding on your "original question" set the percentages of each item in the store, and answering your "modified question" change the structure of my array, this gives you both by modifying your original code.
let myArray = [{"shop":"shop1","item1":"my apple 1","item2":"my carrot 1"},{"shop":"shop1","item1":"my apple 1","item2":"my carrot 1"},{"shop":"shop2","item1":"my apple 0","item2":"my carrot 0"},{"shop":"shop2","item1":"my apple 0","item2":"my carrot 1"}]
let MyArrayDefinition = [{"item":"my apple 0","color":"red","group":"fruit","score":0},{"item":"my carrot 1","color":"orange","group":"vegetable","score": null},{"item":"my apple 1","color":"red","group":"fruit","score":1},{"item":"my carrot 0","color":"orange","group":"vegetable","score":0}]
let k = Object.keys
let items = MyArrayDefinition.reduce((o, v) => (o[v.item] = v, o), {})
let shops = myArray.reduce(function (o, v, i, s) {
return s = v[k(v).find(function (k) {
return k;
})], s = o[s] || (o[s] = {
fruit: 0,
vegetable: 0
}), k(v).forEach(function (k) {
return k.includes('item') && (s[(i = items[v[k]]).group] += i.score);
}), o;
}, {});
// Helper function that calculates percentage
function percentage (amount, total) {
if (total === 0) { // added check for 0 divisor
return `0%`
}
return `${(amount / total) * 100}%`
}
let result = k(shops).map((k, i) => {
let total = Object.values(shops[k]).reduce((a, b) => a + b) | 0 // added check if number else 0
let fruit = shops[k].fruit | 0 // added check if number else 0
let veg = shops[k].vegetable | 0 // added check if number else 0
return {
id: i + 1,
shop: k,
fruit: fruit,
vegetable: veg,
total: total,
fruitPercentage: percentage(fruit, total),
vegetablePercentage: percentage(veg, total)
}
})
console.log(JSON.stringify(result, null, 2))
/** result from console.log()
*
[
{
"id": 1,
"shop": "shop1",
"fruit": 2,
"vegetable": 2,
"total": 4,
"fruitPercentage": "50%",
"vegetablePercentage": "50%"
},
{
"id": 2,
"shop": "shop2",
"fruit": 2,
"vegetable": 0,
"total": 2,
"fruitPercentage": "100%",
"vegetablePercentage": "0%"
}
]
* */
Using map is the way to transpose the data from one array onto another and run calculations if needed.
// Create a function that takes in your result array
function setPercentage (array) {
// Helper function that calculates percentage
function percentage (amount, total) {
return (amount / total) * 100
}
// Map the results of the input array onto a new array,
// and return the result
return array.map((obj) => {
return {
id: obj.id,
shop: obj.shop,
fruit: percentage(obj.itemGroup.fruit, obj.total),
vegetable: percentage(obj.itemGroup.vegetable, obj.total),
total: obj.total
}
})
}
// pass in the result array from your code...
const shops_by_percentage = setPercentage(result)
console.log(shops_by_percentage)
/** result in the console.log()
*
[
{
'id': 1,
'shop': 'shop1',
'fruit': 50,
'vegetable': 50,
'total': 4
},
{
'id': 2,
'shop': 'shop2',
'fruit': 0,
'vegetable': 100,
'total': 1
}
]
*
* */
Below you can find general solution to your problem.
Using this approach you can create unlimited number of items in your items array as well as unlimited number of groups in your definitions and your code will still work as expected. Lastly your score value acts as weight (when you give some item for example score 2 each occurrence will count as two items).
// Your items
const items = [
{
shop: "shop1",
item1: "my apple 1",
item2: "my carrot 1",
},
{
shop: "shop1",
item1: "my apple 1",
item2: "my carrot 1"
},
{
shop: "shop2",
item1: "my apple 0",
item2: "my carrot 0"
},
{
shop: "shop2",
item1: "my apple 0",
item2: "my carrot 1"
},
];
// Your definitions
const definitions = [
{
item: "my apple 0",
color: "red",
group: "fruit",
score: 0
},
{
item: "my carrot 1",
color: "orange",
group: "vegetable",
score: 1
},
{
item: "my apple 1",
color: "red",
group: "fruit",
score: 1
},
{
item: "my carrot 0",
color: "orange",
group: "vegetable",
score: 0
}
];
function groupShops(items) {
return items.reduce((acc, cur) => {
// Find shop with id of current item in accumulator
const currentShop = acc.find(shop => shop.id === cur.shop);
// Get all shop items
const shopItems = Object.keys(cur)
// Filter shop key as it is shop's ID
.filter(key => key !== 'shop')
// Map keys into values
.map(key => cur[key]);
// If shop already exists in accumulator
if (!!currentShop) {
// Return updated accumulator
return acc
// Remove current shop
.filter(shop => shop !== currentShop)
// And add new copy of current shop with new items to the accumulator
.concat({
id: currentShop.id,
items: currentShop.items.concat(shopItems),
});
}
// If shop doesn't exist in accumulator add it there and return updated accumulator
return acc.concat({
id: cur.shop,
items: shopItems,
});
}, []);
};
function extendItems(shops) {
// Filter items which have score 0 or less
const filterItems = items => items.filter(item => item.score > 0);
// Map though shops
return shops.map(shop => {
// Return updated shop
return {
// Keep shop id
id: shop.id,
// Extend itemIds by the properties stored in the definition and filter them
items: filterItems(shop.items.map(item => definitions.find(definition => definition.item === item))),
}
});
}
function calculateResult(shop, index) {
// Get all available groups
const availableGroups = definitions.reduce((acc, cur) => acc.indexOf(cur.group) > -1 ? acc : acc.concat(cur.group), []);
// Calculate total possible score
const getTotalScore = () => shop.items.reduce((acc, cur) => cur.score + acc, 0);
// Get score of a passed group
const getGroupScore = group => shop.items.reduce((acc, cur) => cur.group === group ? acc + cur.score : acc, 0);
// Loop though each available group and get its score
const resultData = availableGroups.reduce((acc, cur) => {
return {
// Copy data from accumulator
...acc,
// Add new property to the accumulator with a property key {group name} and value {percantage}
[cur]: getGroupScore(cur, shop.items) / getTotalScore(shop.items) * 100,
}
}, {});
// Return result object
return {
// Destruct items of the result object
...resultData,
// Store total items count
total: shop.items.length,
// Store shop id
shop: shop.id,
// Store index
id: index,
}
}
// Groups shops
const groupedShops = groupShops(items);
// Groups shops with extended items
const extendedShops = extendItems(groupedShops);
// You result object
const result = extendedShops.map((shop, index) => calculateResult(shop, ++index));
console.log(result);

How to find inside an array of objects the object that holds the highest value?

I have an array that holds several objects named student, each object student has several properties, one of which is an array named grades.
I need to create a function that loops through the student's array and finds which student object has the highest grade inside its grades array.
At the moment I am able to find the highest score, but was not able to understand how to trace back to which student it belongs to.
Here is a snippet of how the function looks like:
function bestStudent() {
var bestGrade = 0;
var student;
for(i=0; i < studentArr.length; i++) {
var student = studentArr[i];
grades = student.grades;
for(g = 0; g <grades.length; g++){
if(grades[g] > bestGrade) {
bestGrade = grades[g];
}
}
}
}
The general idea is the following: you can first map your array of students with their grades to an array of students and their highest grade in order to make it convenient to work with and avoid multiple find-max-grade calculations, and then find the largest of students' highest grade.
Just an example:
var students = [
{
name: "Student 1",
grades: [ 65, 61, 67, 70 ]
},
{
name: "Student 2",
grades: [ 50, 51, 53, 90 ]
},
{
name: "Student 3",
grades: [ 0, 20, 40, 60 ]
}
];
var highestGrades = students.map(function(stud, ind) {
// return a student's name and his highest grade (1)
return {
name: stud.name,
highestGrade: Math.max.apply(Math, stud.grades) // get a student's highest grade
};
// or return index and use it to access original value: (2)
// return {
// index: ind,
// highestGrade: Math.max.apply(Math, stud.grades)
// };
// or return the whole student: (3)
// return {
// student: stud,
// highestGrade: Math.max.apply(Math, stud.grades)
// };
// or just add 'highestGrade' property to object without modifying
// if it's ok for you to have intermediate properties in your object: (4)
// stud.highestGrade = Math.max.apply(Math, stud.grades);
// return stud;
});
// this can be done in O(n), not in O(N * logN) if required:
var bestStudent = highestGrades.sort(function(a, b) {
return b.highestGrade - a.highestGrade;
})[0]; // sort by highest grade desc and return the first (the best) one
// Here we have bestStudent with his name according to map function:
console.log(bestStudent.name + " has the highest score of " + bestStudent.highestGrade); // (1)
// console.log(students[bestStudent.index].name + " has the highest score of " + bestStudent.highestGrade); // (2)
// console.log(bestStudent.student.name + " has the highest score of " + bestStudent.highestGrade); // (3)
// console.log(bestStudent.name + " has the highest score of " + bestStudent.highestGrade); // (4)
You can rewrite this code so that it returns the whole student as the result, or its index, or its specific properties. You can also just add highestGrade property to original object if it's ok for your objects to have an additional intermediate property. It is up to you, the idea doesn't change :)
This code is pretty long, but it is readable and makes the idea of algorithm clear, it is very important since you are a beginner.
If you and your team are fans of shorter but more complex code, then you can easily rewrite it.
Just something like this:
var students = [
{
name: "Student 1",
grades: [ 65, 61, 67, 70 ]
},
{
name: "Student 2",
grades: [ 50, 51, 53, 90 ]
},
{
name: "Student 3",
grades: [ 0, 20, 40, 60 ]
}
];
var bestStudent = students.map(function(stud) {
stud.highestGrade = Math.max.apply(Math, stud.grades);
return stud;
}).sort(function(a, b) {
return b.highestGrade - a.highestGrade;
})[0];
console.log(bestStudent);
By using the same function, you can store the position or the appropriate field
function bestStudent() {
var bestStudent = {};
bestStudent.bestGrade = 0;
var student;
for(i=0; i < studentArr.length; i++) {
var student = studentArr[i];
grades = student.grades;
for(g = 0; g <grades.length; g++){
if(grades[g] > bestStudent.bestGrade) {
bestStudent.bestGrade = grades[g];
bestStudent.name = studentArr[i].name;
}
}
}
return bestStudent;
}
use lodash.js to make thing easy :)
var students=[{Grades:[1,2,3]},{Grades:[5,4,3]},{Grades:[7,77,4]}];
var studentWithBestGrade=_.map(students,function(student,position){
return [position,_.max(student.Grades)];
});
console.log(studentWithBestGrade) //[[0,3],[1,5],[2,77]]
find it JSFIDDLE
var students = [
{
name: "Student 1",
grades: [
90, 98, 80
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
},
{
name: "Student 2",
grades: [
75, 85, 79
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
}
,
{
name: "Student 3",
grades: [
75, 85, 99
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
}
];
var student = students.sort(function(f, l) {
return l.getMyHighest() - f.getMyHighest();
})[0];
console.log(student);
You could use an array for the students with the best grade, if you have more than one.
Then check if the maxGrade is the same as the bestGrade, then push the actual student to the result set and countinue the for loop.
If maxGrade is greater than bestGrade, store the value an put the actual student in a new array.
function bestStudents(studentArr) {
var bestGrade = 0,
bestStudents = [],
i,
maxGrade;
for (i = 0; i < studentArr.length; i++) {
maxGrade = Math.max.apply(null, studentArr[i].grades);
if (maxGrade === bestGrade) {
bestStudents.push(studentArr[i]);
continue;
}
if (maxGrade > bestGrade) {
bestGrade = maxGrade;
bestStudents = [studentArr[i]];
}
}
return bestStudents;
}
var students = [{ name: "Student 1", grades: [90, 98, 99] }, { name: "Student 2", grades: [75, 85, 79] }, { name: "Student 3", grades: [75, 85, 99] }];
console.log(bestStudents(students));
.as-console-wrapper { max-height: 100% !important; top: 0; }
My solution would be using reduce in JS
Only get one student:
var bestStudent = students.reduce(function(a, student){
return Math.max.apply(null, student.grades) >
Math.max.apply(null, a.grades) ?
student : a;}, students[0]);
console.log('Best student is: ' + bestStudent.name + ' with score: '
+ Math.max.apply(null, bestStudent.grades));
Example: https://jsfiddle.net/ahx8jh5g/
In case, you want to get all students which have best grade, let's store it in an array:
var bestStudents = students.reduce(function(a, student){
var maxGradeStu = Math.max.apply(null, student.grades),
maxGradeCur = a.length > 0 ? Math.max.apply(null, a[0].grades) : 0;
if (maxGradeStu === maxGradeCur) {
return a.concat(student);
}
return maxGradeStu > maxGradeCur ? [student] : a;
}, [])
bestStudents.forEach( bestStudent => console.log('Best student is: ' bestStudent.name + ' with score: '
+ Math.max.apply(null, bestStudent.grades)));
For more detail, you can see reduce here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Iterating through an array and sum a value based on a condition in lo-dash

I have a data set which i've simplified :
var data = [{
size: 400
}, {
size: 500
}, {
size: 800
}, {
size: 400
}, {
size: 400
} {
size: 300
}, {
size: 300
}, {
size: 800
}];
var windowWidth = 800;
Using lo-dash how would I make a new array based on how many of size would fit into windowWidth.
the preferedfinal dataset will look like this
var newArray = [{
group: [0],
size: 400
}, {
group: [1],
size: 500
}, {
group: [2],
size: 800
}, {
group: [3, 4],
size: 800
}, {
group: [5,6],
size: 600
}, {
group: [7],
size: 800,
}
The key group refers to the indexes of data[n] that meet the condition of lessthan windowwidth.
the key size refers to the accumulated size of the group.
Note: data[n] just counts up and doesnt repeat for ever go back to data[0].
does data[n] fit inside window?
yes? push to group; go to data[n +1 ].size; check expression
no? create new group(); go to data[n +1].size; check expression
var sum = _.chain(data).pluck('size').reduce(function(result, val, i) {
if (_.isEmpty(result) || val + _.last(result).size > windowWidth)
result.push({ size: val, groups: [i] });
else
{
var last = _.last(result);
last.size += val;
last.groups = last.groups || [];
last.groups.push(i);
}
return result;
}, []).value();
http://jsfiddle.net/219g9hqm/
Looks like I figured out what you need:
var data = [{size: 400}, {size: 500}, {size: 800}, {size: 400}, {size: 400}, {size: 300}, {size: 300}, {size: 800}],
newArray = [],
prevSize = 0,
lastIndex,
windowWidth = 800;
for(var i = 0; i < data.length; i++){
lastIndex = newArray.length - 1;
if(prevSize == data[i].size && (newArray[lastIndex].size + data[i].size < windowWidth)){
newArray[lastIndex].group.push(i);
newArray[lastIndex].size += data[i].size;
}else{
prevSize = data[i].size;
newArray.push({group:[i], size: data[i].size});
}
}
console.log(newArray);
alert(JSON.stringify(newArray));
Not sure if this is the best approach but it only uses lodash functions:
var indexed = _.map(data, function (e, i) {
e['index'] = i;
return e
});
var grouped = _.groupBy(indexed, function (e) {
return e.size
});
var newArray = _.map(grouped, function (e, key) {
return {'size': key, 'group': _.pluck(e, 'index')}
});
Version 2
A less inefficient version using _.reduce for the grouping and _.map for transforming the grouped object into a list of objects. Filters out all the sizes greater than windowWidth.
var windowWidth = 400;
var grouped = _.reduce(data, function (result, e, i) {
if (e.size <= windowWidth) {
var group = result[e.size];
if (!group) {
group = result[e.size] = [];
}
group.push(i)
}
return result;
}, {});
var newArray = _.map(grouped, function (e, key) {
return {'size': key, 'group': e}
});
See Demo

Categories

Resources