I am trying to sort an array into descending order.
This is my current code:
for(var i = 0; i < scoresArray.length; i){
function swap(a, b) {
var temp = scoresArray[a].score;
scoresArray[a] = scoresArray[b].score;
scoresArray[b] = temp;
}
for(var x = 0; x < scoresArray.length; x++){
if(scoresArray[x].score < scoresArray[++x].score){
console.log(x);
swap(x, ++x);
}
}
}
return scoresArray.content;
This is the input array:
[
{ url: 'www.lboro.ac.uk', score: 6 },
{ url: 'www.xyz.ac.uk', score: 3 },
{ url: 'www', score: 8 } ]
This (should be) the output array:
[{ url: 'www.xyz.ac.uk', score: 3 },
{ url: 'www.lboro.ac.uk', score: 6 },
{ url: 'www', score: 8 } ]
Like #Douglas said, using array.sort(compareFunction) makes this easier:
var scoresArray = [
{ url: 'www.lboro.ac.uk', score: 6 },
{ url: 'www.xyz.ac.uk', score: 3 },
{ url: 'www', score: 8 } ];
scoresArray.sort(function(a,b) {
return a.score - b.score;
});
Note that, since scoresArray[i].score are numbers, you can use return a.score - b.score. In a more general case (e.g. if they were strings), you could use
scoresArray.sort(function(a,b) {
if(a.score > b.score) return 1;
if(a.score < b.score) return -1;
return 0;
});
The swap function isn't working, it replaces the values in scoresArray with just the score numbers. It is also important to know that ++x changes the value of x. I think you mean x + 1 instead.
This roughly works:
var scoresArray = [
{ url: 'www.lboro.ac.uk', score: 6 },
{ url: 'www.xyz.ac.uk', score: 3 },
{ url: 'www', score: 8 } ];
function swap(a, b) {
var temp = scoresArray[a];
scoresArray[a] = scoresArray[b];
scoresArray[b] = temp;
}
for(var i = 0; i < scoresArray.length; i++) {
for(var x = 0; x < scoresArray.length - 1; x++) {
if (scoresArray[x].score > scoresArray[x + 1].score) {
swap(x, x + 1);
}
}
}
console.log(scoresArray);
But it would be better to use array.sort:
var scoresArray = [
{ url: 'www.lboro.ac.uk', score: 6 },
{ url: 'www.xyz.ac.uk', score: 3 },
{ url: 'www', score: 8 } ];
scoresArray.sort(function(a, b) {
return b.score - a.score;
});
console.log(scoresArray);
Related
I'm struggling with creating correct data template for my chart. I want it to create template for last 12 months with actual month. For example if I want to have last 12 months i should have data template looking like this:
[{id: BAD,
data: [
{
x: "11",
y: 0
},
{
x: "12",
y: 0
},
{
x: "1",
y: 0
},
...
]},
{id: GOOD,
data: [
{
x: "11",
y: 0
},
{
x: "12",
y: 0
},
{
x: "1",
y: 0
},
...
]},
...
]
It is not that simple because I don't know what to do when month increases to 12 because then its just keeps increasing value of "x" which stands for month and thus I don't know how should I implement that.
What i tried to do was just this. I have no other clues how to get this any hints how I can get that?
const NUMBER_OF_MONTHS = 12;
const getFirstMonth = (date, numOfMonths) => {
date.setMonth(date.getMonth() - numOfMonths);
return date.getMonth();
}
const createDataTemplate = () => {
const template = [];
const firstMonth = getFirstMonth(new Date(), NUMBER_OF_MONTHS)
for (let i = 0; i < RATINGS.length; i++) {
template.push({
'id': RATINGS[i],
'data': []
})
for (let j = 1; j <= NUMBER_OF_MONTHS; j++) {
template[i].data.push({
'x': `${firstMonth + j}`,
'y': 0
})
}
}
return template;
}
I solved it like this, generating an array of the next 12 months/year. Which you can then use to loop over and add to you data
const NUMBER_OF_MONTHS = 12;
const getNextTwelveMonths = () => {
const currentMonth = new Date().getMonth();
// create array with the months until the currentMonth
const months = new Array(currentMonth).fill(null).map((_, i) => i + 1);
// add the last x months to the begin of the array
for (let i = NUMBER_OF_MONTHS; i > currentMonth; i--) {
months.unshift(i);
}
return months;
};
const createDataTemplate = () => {
const template = [];
for (let i = 0; i < RATINGS.length; i++) {
template.push({
id: RATINGS[i],
data: [],
});
const nextMonths = getNextTwelveMonths();
for (let j = 0; j < nextMonths.length; j++) {
template[i].data.push({
x: `${nextMonths[j]}`,
y: 0,
});
}
}
return template;
};
I created a function to check if there are repeated cell phone numbers in a list. The problem is that I did this by using nested for. How could I optimize this code with functional programming ?
checkDuplicate(): boolean {
for (let i = 0; i < this.phoneList.length; i++) {
for (let j = 0; j < this.phoneList.length; j++) {
if (i != j) {
if (this.phoneList[i].number === this.phoneList[j].number) {
this.toastrService.error('Phone already in List!');
return true;
}
}
}
}
return false;
}
You can make a Set containing only the unique numbers and compare the length of the Set to the length of the original array
hasDuplicates(): boolean {
return new Set(this.phoneList.map(p => p.number)).size < this.phoneList.length
}
O(n) solution
It's not a functional but it's the fastest so far.
const checkDuplicate = (phones)=> {
let counts = {};
for(let phone of phones) {
if(counts[phone.number]) return true;
counts[phone.number] = 1;
}
return false;
}
if(checkDuplicate(this.phoneList)) {
this.toastrService.error('Phone already in List!');
}
even better than filter (which i suggested in a comment) use Set - there are a few ways to do it but this is pretty clean. However .filter() would probably be considered more 'functional' as it is a HOC
let a = [1,2,1,3,3,5]
let x = [...new Set(a)]
// => [1, 2, 3, 5]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
You could do something like:
let dups = this.phoneList.filter(item =>
this.phoneList.filter(item2 => item.number == item2.number).length > 1
);
if (dups.length) {
this.toastrService.error('Phone already in List!');
return true;
}
...though it suffers a little for readability.
It's not really about angular but just the JavaScript. You could just short cycle the loop on the list as faster.
Each inner loop is n-i faster (less to do/check) since we already checked those
var xObj = {
phoneList: [{
name: "freddy",
number: 55512121234
}, {
name: "Jimmy",
number: 55512121234
}, {
name: "Mommy",
number: 55512121233
},
{
name: "Tommy",
number: 55512121244
},
{
name: "Luka",
number: 55512121222
},
{
name: "Penny",
number: 55512121255
},
{
name: "Sammy",
number: 55512121266
},
{
name: "Bill",
number: 55512121244
}
],
phoneList2: [{
name: "freddy",
number: 55512121234
}, {
name: "Jimmy",
number: 55512121235
}, {
name: "Mommy",
number: 55512121233
},
{
name: "Tommy",
number: 55512121244
},
{
name: "Luka",
number: 55512121222
},
{
name: "Penny",
number: 55512121259
},
{
name: "Sammy",
number: 55512121266
},
{
name: "Bill",
number: 55512121247
}
],
toastrService: {
error: function(message) {
console.log(message);
}
},
checkDuplicate: function() {
let hasDupe = false
for (let i = 0; i < this.phoneList.length; i++) {
for (let j = i + 1; j < this.phoneList.length; j++) {
if (this.phoneList[i].number === this.phoneList[j].number) {
hasDupe = true;
break;
}
}
if (hasDupe) break;
}
if (hasDupe) this.toastrService.error('Phone already in List!');
return hasDupe;
},
checkDuplicate2: function() {
let hasDupe = false
for (let i = 0; i < this.phoneList2.length; i++) {
for (let j = i + 1; j < this.phoneList2.length; j++) {
if (this.phoneList2[i].number === this.phoneList2[j].number) {
hasDupe = true;
break;
}
}
if (hasDupe) break;
}
if (hasDupe) this.toastrService.error('Phone already in List!');
return hasDupe;
}
};
let cdup = xObj.checkDuplicate();
let cdup2 = xObj.checkDuplicate2();
console.log("dup:", cdup, cdup2);
You can use Array.some to check if a phone number is a duplicate, as shown below. In the loop callback, the phone number is the key of a boolean value added to the exists object. The loop stops as soon as the callback function returns true, which happens when a key/value corresponding to the loop item is found in exists.
checkDuplicate(): boolean {
let exists: { [key: number]: boolean } = {};
return this.phoneList.some(phoneListItem => {
if (exists[phoneListItem.number]) {
return true;
} else {
exists[phoneListItem.number] = true;
return false;
}
});
}
See this stackblitz for a demo.
I need to replace a template array with values from another array and push it to the final result.
This is what I have tried, the problem is that I get the same value when I loop.
var pickups = [
{ address: "Lusaka, Zambia", distance: 22 },
{ address: "Ndola, Zambia", distance: 44 }
];
var template = [{ lable: "distance", data: 0 }];
var final_templates = [];
var pickup_temp = template;
for (var i = 0; i < pickups.length; i++) {
for (var m = 0; m < template.length; m++) {
if (pickup_temp[m].lable == "distance") {
pickup_temp[m].data = pickups[i].distance;
}
}
final_templates.push(pickup_temp);
}
console.log(final_templates);
Expected Result:
[[{lable: "distance", data: 22}],[{lable: "distance", data: 44}]]
Actual Result (same distance value):
[[{lable: "distance", data: 44}],[{lable: "distance", data: 44}]]
It is simpler if the code avoids for loops and just uses Array.map:
var pickups = [
{ address: "Lusaka, Zambia", distance: 22 },
{ address: "Ndola, Zambia", distance: 44 }
];
var template = [{ lable: "distance", data: 0 }];
var final = pickups.map(pick => (
template.map( item => (
{ label: item.lable, data: pick[item.lable] }
))
));
console.log(JSON.stringify(final));
I wanted to write a simple function that allows to roll random item from list, i did it with this code:
this.resources = [false, 'nitrogen', 'silicon', 'cobalt', 'magnesium'];
this.assign_resource = function() {
var index = tools.rnd(0, this.resources.length - 1);
return this.resources[index];
};
But it doesn't play well, so i wanted to change it to different system that allows a list of items (including empty one) and it picks one at random but each one has different chance (for example this one has 10%, this one has 20%). Maybe someone could help me with this kind of function.
Edited -----
for example this could be new list:
this.resources = [
{ type: 'empty', chance: 30 },
{ type: 'nitrogen', chance: 10 },
{ type: 'silicon', chance: 20 },
{ type: 'cobalt', chance: 30 },
{ type: 'magnesium', chance: 10 }
];
How to use it now to make it happen properly?
Edited 2 -----
I am trying to figure out well done programming solution using math rather then simply duplicating items in array, answers presented in this topic are just work arounds to a problem.
I'd solve it by having an array of objects with a chance to be the result, totalling 1.0, then picking a random number between 0 and 1, and then iterating over the resources and check if adding it to a cumulative total includes your random number.
var resources = [
{ resource: false, chance: 0.2 },
{ resource: 'nitrogen', chance: 0.1 },
{ resource: 'silicon', chance: 0.2 },
{ resource: 'cobalt', chance: 0.45 },
{ resource: 'mangesium', chance: 0.05 }
];
function get_result(resouceList) {
//get our random from 0 to 1
var rnd = Math.random();
//initialise our cumulative percentage
var cumulativeChance = 0;
//iterate over our resources
for (var i = 0; i < resouceList.length; i++) {
//include current resource
cumulativeChance += resouceList[i].chance;
if (rnd < cumulativeChance)
return resouceList[i].resource;
}
return false;
}
//test
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
I would set it up so that only the actual resources are in your array and "empty" happens if the random roll falls outside of those.
this.resources = [
{ type: 'nitrogen', chance: 10 },
{ type: 'silicon', chance: 20 },
{ type: 'cobalt', chance: 30 },
{ type: 'magnesium', chance: 10 }
];
this.assign_resource = function() {
var rnd = Math.random();
var acc = 0;
for (var i=0, r; r = this.resources[i]; i++) {
acc += r.chance / 100;
if (rnd < acc) return r.type;
}
// rnd wasn't less than acc, so no resource was found
return 'empty';
}
You can do something like this.
Creating an array with the same value multiple times gives it a higher chance of being selected.
var resources = [{
type: 'empty',
chance: 30
},
{
type: 'nitrogen',
chance: 10
},
{
type: 'silicon',
chance: 20
},
{
type: 'cobalt',
chance: 30
},
{
type: 'magnesium',
chance: 10
}
];
function GetRandom(list) {
var array = [];
for (var i = 0; i < list.length; i++) {
var item = list[i];
var chance = item.chance / 10;
for (var j = 0; j < chance; j++) {
array.push(item.type);
}
}
var idx = Math.floor(Math.random() * array.length);
return array[idx];
}
console.log(GetRandom(resources))
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is how I'd implement the solution.
Step 1: accumulate all the possible chances
Step 2: pick a random value in proportion to total chance
Step 3: loop through the resources to see in which part it random value falls under.
var resources = [
{ type: 'empty', chance: 30 },
{ type: 'nitrogen', chance: 10 },
{ type: 'silicon', chance: 20 },
{ type: 'cobalt', chance: 30 },
{ type: 'magnesium', chance: 10 }
];
function solution(resources) {
let chanceTotal = resources.reduce((acc, val) => { acc += val.chance ; return acc;}, 0);
let randomVal = parseInt(Math.random() * chanceTotal);
let chanceAcc = 0;
let ans;
resources.forEach(r => {
chanceAcc += r.chance;
if (chanceAcc > randomVal && !ans) {
ans = r;
}
});
return ans;
}
console.log(solution(resources));
Here is another implementation.
var res = [
["empty", 3],
["nitrogen", 1],
["silicon", 2],
["cobalt", 3],
["magnesium", 1]
];
var flat = [];
var item;
for(var i = 0, l = res.length; i < l; i++) {
item = Array(res[i][1]+1).join(i+",");
item = item.substr(0, item.length-1);
flat.push(item);
}
flat = flat.join(",").split(",");
function get_random_item() {
var ridx = Math.floor(Math.random() * (flat.length));
return res[flat[ridx]][0];
}
var pick;
for(var p = 0; p < 50; p++) {
pick = get_random_item();
console.log(p, pick);
}
Here is my data:
let a = {
"location": [
{
"data_time": "2016-06-23",
"location_count": 5
},
{
"data_time": "2016-06-23",
"location_count": 120
},
{
"data_time": "2016-06-24",
"location_count": 7
},
{
"data_time": "2016-06-24",
"location_count": 200
},
]
};
And I want to calculate how many data which is below 10, between 10 and 100,or greater than 100
The finally result format would be :
[
{condition: 'Below 10', 20160623: 1, 20160624: 1},
{condition: 'Between 10 and 100', 20160623: 0, 20160624: 0},
{condition: 'Greater than 100', 20160623: 1, 20160624: 1},
]
I stuck for a while how to reach it
Here is what I done :
let l = a.location;
let b = _.chain(l)
.groupBy("data_time")
.map((data, key)=>{
let f1 = _.filter(data,(d)=>{
return d.location_count < 10
})
let f2 = _.filter(data,(d)=>{
return (d.location_count >= 10 && d.location_count < 100)
})
let f3 = _.filter(data,(d)=>{
return ( d.location_count >= 100)
})
return {
date:key ,"10" :f1.length, "100" : f2.length, 'more than 100': f3.length}
})
.value()
console.log(b);
// result
[ {
10: 1,
100: 0,
date: "2016-06-23",
more than 100: 1
},{
10: 1,
100: 0,
date: "2016-06-24",
more than 100: 1
}]
MyJsBin
You can create array with conditions and then use forEach and filter by location_count and add to each object in array.
let a = {
"location": [{
"data_time": "2016-06-23",
"location_count": 5
}, {
"data_time": "2016-06-23",
"location_count": 120
}, {
"data_time": "2016-06-24",
"location_count": 7
}, {
"data_time": "2016-06-24",
"location_count": 200
}]
};
//Create result array with object-conditions
var result = [
{condition: 'Below 10'},
{condition: 'Between 10 and 100'},
{condition: 'Greater than 100'}
];
//Add dates as properties to each object in result
a.location.forEach(function(e) {
var date = e.data_time.replace(/-/g, '');
result.forEach(function(o) {o[date] = 0})
})
//Filter by location_count and add to objects in result
a.location.forEach(function(e) {
var date = e.data_time.replace(/-/g, '');
if(e.location_count < 10) {
result[0][date]++;
} else if(e.location_count >= 10 && e.location_count < 100) {
result[1][date]++;
} else {
result[2][date]++;
}
})
console.log(result)
A proposal in plain Javascript, which uses the logarithm of 10 for grouping.
This works for any count and groups.
var a = { "location": [{ "data_time": "2016-06-23", "location_count": 5 }, { "data_time": "2016-06-23", "location_count": 120 }, { "data_time": "2016-06-24", "location_count": 7 }, { "data_time": "2016-06-24", "location_count": 200 }] },
result = function (array) {
function getCondition(l, first, last) {
function pow10(l) { return Math.pow(10, l); }
return first ? 'Below ' + pow10(l + 1) : last ? 'Greater than ' + pow10(l) : 'Between ' + pow10(l) + ' and ' + pow10(l + 1);
}
var r = [],
groups = Object.create(null),
datesO = Object.create(null),
datesA;
array.location.forEach(function (a) {
var group = Math.floor(Math.log(a.location_count) / Math.log(10)),
date = a.data_time.split('-').join('');
groups[group] = groups[group] || { condition: group };
groups[group][date] = 1;
datesO[date] = true;
});
datesA = Object.keys(datesO).sort();
Object.keys(groups).sort(function (a, b) {
return a - b;
}).forEach(function (a, i, aa) {
var o, j = +aa[i - 1];
if (i) {
while (++j < +a) {
o = { condition: getCondition(j) };
datesA.forEach(function (d) { o[d] = 0; });
r.push(o);
}
}
datesA.forEach(function (d) { groups[a][d] = groups[a][d] || 0; });
groups[a].condition = getCondition(groups[a].condition, i === 0, i + 1 === aa.length);
r.push(groups[a]);
});
return r;
}(a);
console.log(result);