Sort array into "rows" in JavaScript - javascript

I have an array of objects that is currently like this, in which entries are ordered by date and time:
var checkin_data = [
{id: 430, date: "2013-05-05", time: "08:24"},
{id: 435, date: "2013-05-06", time: "04:22"},
{id: 436, date: "2013-05-06", time: "05:36"},
{id: 437, date: "2013-05-06", time: "07:51"},
{id: 488, date: "2013-05-06", time: "08:08"},
{id: 489, date: "2013-05-06", time: "10:12"},
{id: 492, date: "2013-05-06", time: "13:18"},
{id: 493, date: "2013-05-06", time: "15:55"},
{id: 494, date: "2013-05-06", time: "18:55"},
{id: 498, date: "2013-05-06", time: "22:15"},
{id: 501, date: "2013-05-07", time: "11:40"},
{id: 508, date: "2013-05-07", time: "18:00"},
{id: 520, date: "2013-05-08", time: "04:48"},
{id: 532, date: "2013-05-09", time: "21:11"},
{id: 492, date: "2013-05-10", time: "11:45"},
{id: 601, date: "2013-05-11", time: "18:12"}
];
The dates represent a date in a particular week: I'd like to sort this array in order to lay it out in "rows", so the data needs to be re-sorted to lay out like this (note the order of the dates):
var checkin_data = [
{id: 430, date: "2013-05-05", time: "08:24"},
{id: 435, date: "2013-05-06", time: "04:22"},
{id: 501, date: "2013-05-07", time: "11:40"},
{id: 520, date: "2013-05-08", time: "04:48"},
{id: 532, date: "2013-05-09", time: "21:11"},
{id: 492, date: "2013-05-10", time: "11:45"},
{id: 601, date: "2013-05-11", time: "18:12"},
{id: 436, date: "2013-05-06", time: "05:36"},
{id: 508, date: "2013-05-07", time: "18:00"},
{id: 437, date: "2013-05-06", time: "07:51"},
{id: 488, date: "2013-05-06", time: "08:08"},
{id: 489, date: "2013-05-06", time: "10:12"},
{id: 492, date: "2013-05-06", time: "13:18"},
{id: 493, date: "2013-05-06", time: "15:55"},
{id: 494, date: "2013-05-06", time: "18:55"},
{id: 498, date: "2013-05-06", time: "22:15"}
];
Getting the data in that order would allow me to lay out a table like this:
Thanks, any help would be appreciated.

Here is a suggestion using functional methods:
Reduce the list into arrays of buckets based on day, and sort that list (this is like reading the table you've got on rows)
Iterate through the rows in order, clear out unused ones.
Here:
//first, we collapse the array into an array of buckets by day
half_sorted = checkin_data.reduce(function(accum,cur){
var bucket = new Date(cur.date).getDay();
accum[bucket].push(cur);
return accum;
},[[],[],[],[],[],[],[]]).map(function(day){
return day.sort(function(x,y){ // now we sort each bucket
return new Date("01-01-1990 "+x.time) - new Date("01-01-1990 "+y.time);
});
});
// At this point, we have an array with 7 cells looking like your table
// if we look at its columns.
// finally, we push to the result table.
var result = [];
var daysToClear = 7;
for(var i=0;daysToClear>0;i=(i+1)%7){
if(half_sorted[i] && half_sorted[i].length > 0){
result.push(half_sorted[i].pop());
}else if(half_sorted[i] && half_sorted[i].length === 0){
half_sorted[i] = null;
daysToClear--;
}
}
Working fiddle

First of all, I think you're going about this in the wrong way. Please see my note below the following code.
To do exactly as you've asked, here's one way:
// parsing the date strings ourselves avoids time zone problems
function dateFromString(string) {
var parts = string.split('-');
return new Date(parseInt(parts[0], 10),
parseInt(parts[1], 10) - 1,
parseInt(parts[2], 10));
}
The above is a utility function.
var i, l, dates = [[], [], [], [], [], [], []], item;
// place the objects into dow-sorted buckets
for (i = 0, l = checkin_data.length; i < l; i += 1) {
item = checkin_data[i];
dates[dateFromString(item.date).getDay()].push(item);
}
i = 0;
l = 0;
checkin_data = [];
while (true) { // instead of a for loop to handle the row-wrap manually
if (dates[i][l]) {
item = dates[i][l];
checkin_data.push(item);
}
i += 1;
if (i === 7) {
if (!item) {
break; // we had a complete row with no data
}
item = undefined;
l += 1;
i = 0;
}
}
checkin_data is now sorted in the order you requested.
Note: you really don't need the second loop, because it is doing most of the work you'll have to do again to use the provided array. So in your routine for writing out the table, instead just use the data structure that the first loop creates. You would of course need a slightly different bailout strategy since you don't want to create an extra blank row, but I'll leave that up to you.
After a bit of thought, though, I came up with another way to do it, if you don't mind adding a new key to your objects:
function dateFromString(string) {
var parts = string.split('-');
return new Date(parseInt(parts[0], 10),
parseInt(parts[1], 10) - 1,
parseInt(parts[2], 10));
}
var i, l, counts = [0, 0, 0, 0, 0, 0, 0], item, dow;
for (i = 0, l = checkin_data.length; i < l; i += 1) {
item = checkin_data[i];
dow = dateFromString(item.date).getDay();
item.sortKey = ++counts[dow] * 7 + dow;
}
checkin_data.sort(function(a, b) {
return a.sortKey - b.sortKey;
});

I've come up with a solution, maybe not the most elegant, but it's working:
var sorted_data = [], elements_to_dump = [], i, j, tmp;
while (checkin_data.length > 0) {
for (i = 0; i < checkin_data.length; i++) {
if (checkin_data[i-1]) {
if (checkin_data[i-1].date === checkin_data[i].date) {
continue;
}
}
sorted_data.push(checkin_data[i]);
elements_to_dump.push(checkin_data[i].id);
}
for (j = 0; j < elements_to_dump.length; j++) {
for (i = 0; i < checkin_data.length; i++) {
if (checkin_data[i].id === elements_to_dump[j]) {
tmp = checkin_data.splice(i, 1);
break;
}
}
}
}

I'd like to sort this array in order to lay it out in "rows", so the data needs to be re-sorted [into this linear representation]. Getting the data in that order would allow me to lay out a table
No, it does not need to be. Actually, that's one step too much, and the order of your intermediate result makes absolutely no sense. What you should do instead is construct a (weekday-) list of (entries-per-day-) lists:
var days = [];
for (var i=0, date=null, day; i<checkin_data.length; i++) {
var entry = checkin_data[i];
if (entry.date !== date)
days.push(day = []);
day.push(entry);
}
That's it, you have you two-dimensional format now. Well, maybe you will need to transpose it to get it into the table you wanted, but that's not too complicated either:
var header = [],
table = [header]; // or create a DOM or a HTML string or whatever
for (var i=0; i<days.length; i++)
header.push(days[i][0].date /* or the weekday name? */);
for (var r=0; !done; r++) {
var row = [],
done = true;
// create cells:
for (var i=0; i<days.length; i++)
if (days[i].length > r) {
row[i] = days[i][r].time;
done = false;
} else
row[i] = "";
}
if (!done)
table.push(row);
}

What you're trying to do is very simple. This is what I would do:
var groups = groupBy(checkin_data, "date"); // groups items based on date
var table = zipAll.apply(null, toArray(groups)); // zips corresponding elements
// of each group into an array
After this you may create your table as follows:
var header = getHeader(groups), rows = map(table, getRow);
document.body.appendChild(getTable(header, rows));
Of course the actual code would be much bigger (a little more than 100 lines of code) since you need to write the logic for groupBy, toArray, zipAll, map, getHeader, getRow, getTable, etc.
Luckily for you I had the time to go and write all this stuff. Hence you now have a working demo: http://jsfiddle.net/hZFJw/
I would suggest that you browse through my code and try to understand how it works. It's too much to explain in one answer.
Note: My solution may be more than 100 lines of code. However it's still a simple solution because:
The actual code which generates the table is just 4 lines long and is very simple to understand.
The rest of the code is composed of reusable functions like groupBy, zipAll and map. These functions are very small and simple to understand.
Overall by abstracting the program into reusable functions the size of the program has increased. However the complexity of the program has considerably decreased.
You could achieve the same result by tackling the problem in an imperative style like most other answers do. However doing so makes the program harder to understand. Compare my code with other answers and see for yourself.

You can sort array with Alasql JavaScript library.
alasql.fn.WEEKDAY = function(d) { // User-defined function
return (new Date(d)).getDay();
};
var res = alasql('SELECT *, WEEKDAY(date) AS dow FROM ? ORDER BY dow', [checkin_data]);
Try this example at jsFiddle.

Related

Javascript runtime optimizing array modifications

I have a problem optimizing the runtime for my code. Ideally, what I'm trying to achieve is that all the operations below is performed in a single loop, so that I don't have to run through the dataset many times as I'm doing now (very large dataset!)
The code is transforming aggData to an array on the following format: [0: 0, 1: 0, 2: 0, 3: 43, 4: 121, 5: 0, ....], where each number represents a year in the interval, if the interval is (1800-2020) 0 will represent the count for 1800, 1 will be 1801 and so on ..
aggData is an array of objects on the following format: {key_as_string: "1900-01-01T00:00:00.000Z", key: -2208988800000, doc_count: 17}. The start-year is the first year with a doc_count higher than 0.
Below I provide a description of what each step does as the code is now:
Here I am changing the format of each object in the list to be : {year: number, count: number}
const formatAggData = aggData
.map((item: AggData) => {
return { year: moment(item.key_as_string).year(), count: item.doc_count };
});
This function creates an array of objects with the from as start year and to as end year, if the year already exists in existingArray it uses the count from there, if not it sets count to 0.
function fillYears(from: number, to: number, existingArray: YearCount[]) {
const existingObject: { [year: string]: number } = { year: null };
existingArray.map((x: YearCount) => (existingObject[x.year] = x.count));
const yearsArray = [];
for (let i = from; i <= to; i++) {
yearsArray.push({
year: i,
count: existingObject[i] || 0,
});
}
return yearsArray;
}
Converts year values to count-values, where the first year in the list will be 0 with the corresponding value, second will be 1 with corresponding value and so on..
const resultList = fillYears(min, max, formatAggData).map(
(item: YearCount) => item.count,
);
I was looking at you code. can't you do it like this? it looks like you don't need to know the year at this moment
function fillYears(from: number, to:number, existingYears: YearCount[]) {
for (let i = from; i <= to; i++) {
yearsArray.push({
year: i,
count: existingYears[i].doc_count || 0,
});
}
}

I want to add a new variable called Averagetimespent in the details obj inside the following javascript array. Could anyone help me out with this?

Following is the javascript array that I am using to populate an extra field called AverageTimeSpent. But somehow I am not able to convert it into the right format and add it accordingly.
-I tried different options. These are seen in the comments that I have added among the lines of code for entering the data.
Tried converting the array into a string and parsing it and the console loggin it and also vice versa.
None of them seem to be working
var Truck_data = [
{
date: "15/12/19",
details: [
{
driver_name: "ram",
truck_number: "KA 03 2132",
distance_travelled: 50,
orders_delivered: 45,
orders_missed: 10
},
{
driver_name: "raju",
truck_number: "KA 03 2354",
distance_travelled: 30,
orders_delivered: 15,
orders_missed: 1
}
{
date: "12/12/19",
details: [
{
driver_name: "ram",
truck_number: "KA 03 2132",
distance_travelled: 50,
orders_delivered: 65,
orders_missed: 20
},
{
driver_name: "raju",
truck_number: "KA 03 2354",
distance_travelled: 30,
orders_delivered: 65,
orders_missed: 34
}
]
}
]
for (var j in Truck_data) {
console.log(i);
for (var i in Truck_data.details) {
var temp = Math.random() * 100;
// Truck_data.details[i].Avg_time_spent = 0;
// Truck_data.details[i].Avg_time_spent = temp;
Truck_data.details[i].push(Avg_time_spent, temp);
console.log(Truck_data);
//TruckOrder.push(array);
}
}
Your for loop should be something like this:
for (var j of Truck_data) {
var innerDetails = j.details;
for (var i of innerDetails) {
var temp = Math.random() * 100;
i['Avg_time_spent'] = temp;
}
}
Previously you're using 'for in' and you should use 'for of' for what you're trying to do.
See this

Search and replace value of object property from one array with value of object property from second array?

I have two arrays of objects say 1- variants and 2- inventoryLevels. Objects in both arrays share a property which is the id. So I want to search for each variant if it's id is matched with any inventoryLevel I want to change its property named shopify_inventory_quantity with matched inventoryLevel's property available ? My words are little but confusing but take a look at code below basically it's doing properly whats needed I just want to know can it be optimized right now it's nested for loop. So any help to make it efficient would be appreciated ?
for (let i = 0; i < variants.length; i++) {
for (let j = 0; j < inventorylevels.length; j++) {
if (variants[i].id === inventorylevels[j].variant_id) {
variants[i].shopify_inventory_quantity = inventorylevels[j].available;
}
}
}
I understand you have a solution in O(n²). Assuming your ids are unique, you can reduce the time complexity to O(n) (basically what #Alireza commented):
var variants = [
{id: 0, shopify_inventory_quantity: 0},
{id: 1, shopify_inventory_quantity: 0},
{id: 2, shopify_inventory_quantity: 0}
];
var inventoryLevels = [
{id: 0, available: 10},
{id: 1, available: 2},
{id: 2, available: 3}
];
// O(n) + O(n) = O(n)
function getAvailableVariants(v, i) {
// O(n)
var inventoryLevels = i.reduce(function(inventoryLevels, inventoryLevel) {
inventoryLevels[inventoryLevel.id] = inventoryLevel;
return inventoryLevels;
}, {});
// O(n)
return v.map(variant => Object.assign(variant, {shopify_inventory_quantity: inventoryLevels[variant.id].available}));
}
var results = document.createElement('pre');
results.textContent = JSON.stringify(getAvailableVariants(variants, inventoryLevels), null, '\t');
document.body.appendChild(results);

best combination algorithm of complex data with multiple contraints

I guess we can say that this is very similar to an already asked question here (Optimisation/knapsack algorithm with multiple contraints in JavaScript), which hasn't yet an answer.
Let say we like javascript, C, C++, java. Any of this languages work for me. Anyone know algorithms to solve the problem?
PROBLEM:
find the best subset of items which grants minimum cost and maximum number of objects, knowing that there's a limitation of resource:
var items = [
{name: "Rome", cost: 1000, hours: 5, peoples: 5},
{name: "Venice", cost: 200, hours: 1, peoples: 10},
{name: "Torin", cost: 500, hours: 3, peoples: 2},
{name: "Genova", cost: 700, hours: 7, peoples: 8},
{name: "Rome2", cost: 1020, hours: 5, peoples: 6},
{name: "Venice2", cost: 220, hours: 1, peoples: 10},
{name: "Torin2", cost: 520, hours: 3, peoples: 2},
{name: "Genova2", cost: 720, hours: 7, peoples: 4},
{name: "Rome3", cost: 1050, hours: 5, peoples: 5},
{name: "Venice3", cost: 250, hours: 1, peoples: 8},
{name: "Torin3", cost: 550, hours: 3, peoples: 8},
{name: "Genova3", cost: 750, hours: 7, peoples: 8}
];
var maxCost = 10000, maxHours = 100, maxPeoples = 50;
// find subset of items that minimize cost, hours and peoples
// and maximize number of items
// do not exceed max values!!!
IDEAS I HAD: I imagined I could do a solution to knapsack problem for each couple of cost (let call them "KPcost-hours", "KPhours-cost", "KPcost-peoples" etc.), which grants me the solution to optimize single costs. Then, if I'm lucky, take the common parts of this subsets and work from there... but i don't think it's a good path...
If you can give a script sample, or a pseudo-script sample, you're welcome! Thank you!
General solution
PROBLEM: find the best subset of items which grants minimum cost and
maximum number of objects, knowing that there's a limitation of
resource.
I see two optimization criteria here (I'll talk about the case where you want to minimize people, hours and cost below as well).
A possible approach is to build a program that will return a maximum Pareto-optimal set of solutions.
A Pareto set is a set of non-dominating solutions, meaning that for any two solutions S1 and S2, S1 does not dominate S2, and vice versa. A solution S1 dominates a solution S2 if it is better or equal than S2 regarding all criterias, and strictly better regarding at least one criteria.
For example, in your case, we can consider the following solutions:
S1: cost = 10, nb_objects = 4
S2: cost = 10, nb_objects = 7
S3: cost = 0, nb_objects = 0
S4: cost = 14, nb_objects = 6
Then our Pareto-optimal set of solutions is {S1, S3, S4}. That is because they do not dominate each other (for example, S1 does not dominate S4 because S4 is better regarding the number of objects). S2 is not part of the Pareto-optimal solution because it is dominated by both S1 and S4.
In the general case, Pareto-set are very hard to calculate, and can be extremely big. In your particular case, 4 criteria seem somehow reasonable, but it always can be surprising.
Here is a pseudocode on how to compute such a set of solutions:
Result = array of size nb_objects, initialized with empty sets
for i from 0 to total_nb_of_objects:
for each feasible solution 'new_solution' to the problem with fixed number of objects:
for each solution of Result[i]:
if hours(new_solution) >= hours(solution) and \
cost(new_solution) >= cost(solution) and \
people(new_solution) >= people(solution):
dominated = true
break
if not dominated:
add new_solution to Result[i]
return Result
This little pseudocode here has more a value to try and understand the concept of Pareto Efficiency, I would not advice looping on all the feasible solutions of a variation to a knapsack problem (too costy).
A brute force approach by checking all combinations.
function getItems(array, constraints, [optimum, equal]) {
function iter(index = 0, right = [], add) {
function update() {
if (!result.length || optimum(right, result[0])) return result = [right];
if (equal(right, result[0])) result.push(right);
}
if (index >= array.length || !constraints.every(fn => fn(right))) return;
if (add && right.length) update();
var temp = right.find(({ ref }) => ref === array[index]),
old = JSON.parse(JSON.stringify(right));
if (temp) {
temp.count++;
} else {
right.push({ count: 1, ref: array[index] });
}
iter(index, right, true);
iter(index + 1, old);
}
var result = [];
iter();
return result;
}
const
addBy = k => (s, { count, ref: { [k]: value } }) => s + count * value,
addCount = (s, { count }) => s + count;
// find subset of items that minimize cost, hours and peoples
// and maximize number of items
// do not exceed max values!!!
var items = [{ name: "Rome", cost: 1000, hours: 5, peoples: 5 }, { name: "Venice", cost: 200, hours: 1, peoples: 10 }, { name: "Torin", cost: 500, hours: 3, peoples: 2 }, { name: "Genova", cost: 700, hours: 7, peoples: 8 }],
maxCost = 10000,
maxHours = 100,
maxPeoples = 50,
result = getItems(
items,
[
array => array.reduce(addBy('cost'), 0) <= maxCost,
array => array.reduce(addBy('hours'), 0) <= maxHours,
array => array.reduce(addBy('peoples'), 0) <= maxPeoples
],
[
(a, b) => a.reduce(addCount, 0) > b.reduce(addCount, 0),
(a, b) => a.reduce(addCount, 0) === b.reduce(addCount, 0)
]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming one can only select 0 or 1 of each item, there are 2^12=4096 combinations possible. The number of feasible solutions is 3473. The number of non-dominated (or Pareto optimal) solutions is 83.
I used two different approaches:
Enumerate all feasible solutions. Then filter out all dominated solutions (each solution must be better in at least one objective than all other solutions).
Write a Mixed Integer Programming. It finds a solution, and adds a constraint that says: it should be better in at least one of the objectives than previous solutions. (Along the lines of this model).
Both methods find these 83 solutions. For this problem complete enumeration is faster.
Note that the number of Pareto optimal solutions can grow quickly. Here are some pictures of such a Pareto optimal set of a real-world design problem.
Note that there is no "single" best solution. All Pareto optimal solutions are optimal. Only when you make assumptions on the trade-offs between objectives, you can reduce the number of optimal solutions further.
I elaborate a working solution, but it's really bruteforce, however a bit optimized. I didn't went thru the Pareto solution which I believe is probably a better solution. Unfortunately the script from Nina Sholz didn't work (at least for me), so I came up with this one.
Just to leave here a working sample (read: don't use for BIG data).
PS - if anyone can write any phrase in a better english, comment below, I'll correct my bad writing.
/**
* Brute Force approach
* Problem: find combination of data objects to minimize sum of object properties and maximize number of objects
* Costraint: sum of object properties has upper limit (for each property)
* Solution used: do every combination, starting with the max number of objects, then lower by 1 each time, until a (or more) combination satisfy every criteria.
*/
// combination
// e.g. combination of 3 numbers with value from 0 to 4 -> combination(3,5)
// see https://rosettacode.org/wiki/Combinations#JavaScript
function combination(n, length) {
// n -> [a] -> [[a]]
function comb(n, lst) {
if (!n) return [[]];
if (!lst.length) return [];
var x = lst[0],
xs = lst.slice(1);
return comb(n - 1, xs).map(function (t) {
return [x].concat(t);
}).concat(comb(n, xs));
}
// f -> f
function memoized(fn) {
m = {};
return function (x) {
var args = [].slice.call(arguments),
strKey = args.join('-');
v = m[strKey];
if ('u' === (typeof v)[0])
m[strKey] = v = fn.apply(null, args);
return v;
}
}
// [m..n]
function range(m, n) {
return Array.apply(null, Array(n - m + 1)).map(function (x, i) {
return m + i;
});
}
var fnMemoized = memoized(comb),
lstRange = range(0, length-1);
return fnMemoized(n, lstRange)
}
// just some math calculation ------
// obviously n & r in N; r < n
function _factor(n){
var f = 1;
while (n > 1){ f *= n--; }
return f;
}
function _factor2(n,to){
var f = 1;
while (n > 1 && n >= to){ f *= n--; }
return f;
}
function _factorFraction(sup,inf){
return (sup > inf) ? _factor2(sup,inf+1) : 1/_factor2(inf,sup+1)
}
function _combination(n,r){
return (r > n/2) ? _factorFraction(n,r)/_factor(n-r) : _factorFraction(n,n-r)/_factor(r); // namely _factor(n)/_factor(n-r)/_factor(r)
}
// just some math calculation ------
var minr = 2, // set inferior limit (r) of combination search. 2 <= minr < datas.length
datas = [], // to be set. matrix to be filled with array of data
limits = [0], // to be set. contains limit for each datas column
comboKeep = [], // will contain all solutions found
columns,
sums,
timer;
function combineCheck(r){
if (r < minr) return;
console.log("Expected number of combination C(",datas.length,",",r,") = ",_combination(datas.length,r));
var metconditions = 0;
var CNR = combination(r,datas.length);
CNR.forEach(combo => {
sums = new Array(columns).fill(0);
// calculate sum for each column
for (var j=0; j<combo.length; j++){
for (var i=0; i<columns; i++){
sums[i] += datas[combo[j]][i];
};
}
// check if conditions are met
for (var i=0; i<columns; i++){
if (sums[i] > limits[i]){
//console.log("sum of column",i,"exceeds limit (",sums[i]," > ",limits[i],")");
return;
}
};
comboKeep.push(combo);
metconditions++;
});
console.log("Condition met in ",metconditions,"combos.");
if (metconditions == CNR.length){
console.log("No need to go further, all combo have been checked.");
return;
}
//------------
// OPTIONAL...
//------------
if (metconditions) return; // remove this line if you want all possible combination, even with less objects
combineCheck(r-1); // for delayed call: setTimeout( () => combineCheck(r-1), 250 );
}
function combineCheckStarter(){
comboKeep = [];
columns = datas[0].length;
timer = Date.now();
combineCheck(datas.length-1);
timer = Date.now() - timer;
}
//-----------------------------------------
var items = [
{name: "Rome", cost: 1000, hours: 5, peoples: 5},
{name: "Venice", cost: 200, hours: 1, peoples: 10},
{name: "Torin", cost: 500, hours: 3, peoples: 2},
{name: "Genova", cost: 700, hours: 7, peoples: 8},
{name: "Rome2", cost: 1020, hours: 5, peoples: 6},
{name: "Venice2", cost: 220, hours: 1, peoples: 10},
{name: "Torin2", cost: 520, hours: 3, peoples: 2},
{name: "Genova2", cost: 720, hours: 7, peoples: 4},
{name: "Rome3", cost: 1050, hours: 5, peoples: 5},
{name: "Venice3", cost: 250, hours: 1, peoples: 8},
{name: "Torin3", cost: 550, hours: 3, peoples: 8},
{name: "Genova3", cost: 750, hours: 7, peoples: 8}
];
var datas = Array.from(items, e => [e.cost, e.hours, e.peoples]);
var limits = [2500, 8, 20];
//-----------------------------------------
// test ;)
combineCheckStarter();
console.log("Combination found in ",timer,"ms:",comboKeep);
// pretty print results
var prettier = new Array(comboKeep.length),
unifier = new Array(columns).fill(0);
comboKeep.forEach( (combo, k) => {
var answer = new Array(combo.length);
sums = new Array(columns).fill(0);
combo.forEach((itm,i) => {
answer[i] = items[itm].name;
for (var j=0; j<columns; j++){
sums[j] += datas[itm][j];
};
});
prettier[k] = {items: answer.join(","), cost: sums[0], hours: sums[1], peoples: sums[2]};
for (var j=0; j<columns; j++){
if (unifier[j]<sums[j]) unifier[j] = sums[j];
};
});
// normalize
prettier.forEach( e => {
e.total = e.cost/unifier[0] + e.hours/unifier[1] + e.peoples/unifier[2];
});
//find the best (sum of all resource is lower)
prettier.sort( (b,a) => b.total-a.total);
console.log("sorted solutions:",prettier);
console.log("Best solution should be ",prettier[0].items,prettier[0]);

Calculate the average of points in a array - Javascript

I have an array with infos about a group of people : name, current status, new points, last event points
Example:
var group = new Array();
group[0] = "John Doe,beginer,14,7";
group[1] = "Lois Lane,advanced,13,9";
group[2] = "Bruce Waine,advanced,17,10";
I need a function that calculates the average of the new points.
For the previous example the average would be (14+13+17)/3 = 14.66666666666667
It'd be a heck of a lot easier if you convert the data in the array from strings to objects This will benefit you in two ways: 1) the code will be more readable, understandable, and easier to maintain, and 2) you won't have to do a bunch of string gymnastics to pull out the relevant data.
Do something like this:
var group = [
{ name: 'John Doe', status: 'beginner', newPoints: 14, eventPoints: 7 },
{ name: 'Lois Lane', status: 'advanced', newPoints: 13, eventPoints: 9 },
{ name: 'Bruce Waine', status: 'advanced', newPoints: 17, eventPoints: 10 }
];
function getAverageNewPoints(people) {
var count = people.length || 0,
average = 0;
for (var i = 0; i < count; i++) {
average += people[i].newPoints;
}
return average / count;
}
alert('The average of new points in the group is: ' + getAverageNewPoints(group));
Try the following:
function groupAverage(group) {
var sum = 0;
var count = group.length;
for (var i in group) {
sum += parseInt(group[i].split(',')[2], 10);
}
return sum / count;
}
Split the String at , and get the values and convert them to Number.
var group = new Array();
group[0] = "John Doe,beginer,14,7";
group[1] = "Lois Lane,advanced,13,9";
group[2] = "Bruce Waine,advanced,17,10";
sum=0;
for(var i in group)
{
sum=sum+Number(group[i].split(",")[2]);
}
console.log(sum/group.length);
You have a bad data structure for this. You don't want to use strings. You also should not use the Array constructor. Start with:
var group = [
{name: "John Doe", rank: "beginner", points: 14, lastScore: 7},
{name: "Lois Lane", rank: "advanced", points: 13, lastScore: 9},
{name: "Bruce Wayne", rank: "advanced", points: 17, lastScore: 10},
],
length = group.length,
sum = 0,
i;
for ( i = 0; i < length; ++i) {
sum += group[i].points;
}
return sum / length; // Or do something else with the result.
// I did no error checking.
You could use an object constructor instead of the inline Object I used, but that's not really necessary. I'm just curious; did you use strings as a default, or was using a string interface part of a textbook assignment?
Oh, one reason to use [] instead of new Array() is that when you construct an Array, the value is always truthy, while [] is falsy.
I did take the liberty of correcting Bruce's last name.

Categories

Resources