I'm having trouble displaying my randomly selected object - javascript

I am having trouble displaying the random object and the properties of that random object. The goal of this project is to have a list of stockItems, and when I press a button, it selects a determined number of those objects and displays them in an HTML p tag. Right now when I try to display it, it prints out as [object]. The goal is to have the properties of the selected object on different lines.
Here is the code I am working with:
function buildShopItems(count) {
var shopItems = [], i, itemIndex;
count = stockItems.length < count ? stockItems.length : count;
function getUniqueRandomItem() { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (shopItems.indexOf(item) < 0) return item;
}
}
for (i = 0; i < count; i++) {
shopItems.push(getUniqueRandomItem());
}
return shopItems;
console.log(shopItems);
}
var stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
//...
];
var shopItems = buildShopItems(1);
console.log(shopItems);
document.getElementById("item").innerHTML = shopItems.item;
document.getElementById("type").innerHTML = shopItems.type;
document.getElementById("weight").innerHTML = shopItems.weight;
document.getElementById("cost").innerHTML = shopItems.cost;

The problem was with your usage of indexOf. You can use indexOf to search for an object because in javascript you can't compare object using == or === and indexOf uses ===. Also made some syntax updates for you.
'use strict'
const stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
];
function isEquivalent(a, b) {
// Create arrays of property names
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (let i = 0; i < aProps.length; i++) {
const propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
// normal indexof will not work with object because it uses strict equality
function myIndexOf(array, object) {
for (let i = 0; i < array.length; i++) {
if (isEquivalent(array[i], object)) return i;
}
return -1;
}
function getUniqueRandomItem(shopItems) { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (myIndexOf(shopItems, item) < 0) return item;
}
}
function buildShopItems(count) {
count = stockItems.length < count ? stockItems.length : count;
const shopItems = [];
for (let i = 0; i < count; i++) {
const item = getUniqueRandomItem(shopItems);
shopItems.push(item);
}
return shopItems;
}
const shopItems = buildShopItems(1);
console.log(shopItems);

Related

Find unknown number in array of containing consecutive numbers javascript

I have an array like this;
var specialOne = 3;
var array = [{value:"special"},{value:"1"},{value:"2"},{value:"specialOne"},{value:"4"},{value:"special"}];
And I need to convert it to this array;
var temp = [{value:"0"},{value:"1"},{value:"2"},{value:"3"},{value:"4"},{value:"5"}];
special's should be replaced with the appropriate value.
specialOne should be replaced with the given number.
How can i do this ?
More examples:
0,special,2,special,4,5 => 0,1,2,3,4,5
7,8,9,special => 7,8,9,10
special,special,10 => 8,9,10
Maybe this is what you are after
var specialOne = 3;
var array1 = [{value:"special"},{value:"1"},{value:"2"},{value:"specialOne"},{value:"4"},{value:"special"}];
function addspec(specialOne){
array1.forEach((o,i)=>{
if(o.value=="specialOne")o.value = specialOne.toString()
if(o.value=="special") o.value = array1[i-1]?(parseInt(array1[i-1].value)+1).toString():"0"
})
}
addspec(3)
console.log(array1)
This may help you
var specialOne = 3;
var array = [
{ value: "special" },
{ value: "1" },
{ value: "2" },
{ value: "specialOne" },
{ value: "4" },
{ value: "special" }
];
for (i = 0; i < array.length; i++) {
if (array[i].value == 'specialOne') {
array[i].value = String(specialOne);
console.log(array);
} else if (array[i].value == 'special') {
array[i].value = String(0);
array.pop()
array.push( { value: String(5) } );
}
}
for (let i = 0; i < array.length; i++) {
if (array[i].value === "special") array[i].value = i.toString();
if (array[i].value === "specialOne") array[i].value = specialOne.toString();
};
When you need to modify an array you should try to use a 'for' loop first. It is the most efficient as it will not modify the index of the array and will not return a new array (Imagine you have to 10 000 items to modify in your array... ).
Here it's very simple, you itere the array, if the condition match you modify the value (of the array itself).
Something like this?
var specialOne = 3;
var array = [{
value: "special"
}, {
value: "1"
}, {
value: "2"
}, {
value: "specialOne"
}, {
value: "4"
}, {
value: "special"
}];
function getValues(array) {
let counterSync = 0;
let backCheck = [];
let inFlow = false;
let backCheckOnce = false;
return array.map((m, i) => {
if (isNaN(parseInt(m.value))) {
if (inFlow || window[m.value]) {
m.value = "" + (window[m.value] || (counterSync + 1));
} else {
if (i === 0) {
backCheckOnce = true;
}
backCheck.push(m);
}
} else {
inFlow = true;
// do reverse check only once
if (backCheckOnce) {
backCheck.reverse().forEach((x, idx) => x.value = "" + (+m.value - 1));
backCheckOnce = false;
}
}
counterSync = +m.value;
return m;
});
}
console.log(getValues(array));

JS: Is using a dictionary faster than looping through an array?

I'm building some program in Nodejs, which will need to keep track in memory of a large number of users. Also, i will have a function that filters a user by id. The code would look something like this:
const users = [
{
id: 1,
name: 'John',
friends: [3, 6, 8]
},
{
id: 2,
name: 'Mark',
friends: [567, 23]
}
]
function getUserById(userId) {
const user = users.filter(user => user.id === userId);
return user[0];
}
The question is, whether this version is generally faster(each key is user id):
const users = {
1: {
id: 1,
name: 'John',
friends: [3, 6, 8]
},
2: {
id: 2,
name: 'Mark',
friends: [567, 23]
}
}
function getUserById(userId) {
return users[userId];
}
My intuition says that the dictionary is faster. What are the facts?
Key lookup time in objects is not guaranteed. It might also be O(n), but most engines will optimize it towards O(1) if you dynamically look up a key multiple times. Filtering an array is O(n), .find() however is twice faster on average:
return users.find(user => user.id === userId);
Now the only datastructure that guarantees O(log n) lookup are Maps:
const userMap = new Map(users.map(u => [u.id, u]));
console.log(userMap.get("test"));
If you however plan to do that in a very large scale (100k is large), I would rather move that task to a database, as it is heavily optimized for those tasks. MongoDB would be easy to adopt, Redis would be very fast, there are many others out there.
I've written a small script that can be copy pasted to the console and shows the actuall data for this question and varifies in practice the answer of Jonas Wilms.
function random_int_from_range(x, y) {
return (x + Math.floor(Math.random() * (y - x + 1)));
}
function generate_name(length_min, length_max) {
var letters = 'abcdefghijklmnopqrstuvwxyz';
var name_array = [];
for (var i = 0; i <= random_int_from_range(length_min, length_max); i ++) {
name_array.push(letters.charAt(Math.floor(Math.random() * letters.length +1)));
}
return name_array.join('')
}
function generate_friends_array(length_min, length_max, num_users) {
friends_array = [];
for (var i = 0; i < random_int_from_range(length_min, length_max); i++) {
friends_array.push(random_int_from_range(0, num_users - 1))
}
return friends_array
}
function generate_users_dict(num_users) {
var users = {};
for (var i = 0; i < num_users; i++) {
users[i] = {
'id': i,
'name': generate_name(4,6),
'friends': generate_friends_array(0, 20, num_users)
}
}
return users
}
function generate_users_list_from_dict(users_dict) {
var users_list = [];
for (var key in users_dict) {
users_list.push(users_dict[key]);
}
return users_list;
}
function get_diff_in_seconds_from_two_milisecond_values(early_value, late_value) {
return (late_value - early_value) / 1000
}
function get_user_by_id_from_dict(users_dict, user_id) {
return users_dict[user_id]
}
function get_user_by_id_from_list(users_list, user_id) {
const users = users_list.filter(user => user.id === user_id);
return users[0]
}
function get_time_for_retrieval_of_item_from_object(object, object_length) {
var function_names = ['get_user_by_id_from_dict', 'get_user_by_id_from_list'];
var random_id = random_int_from_range(0, object_length - 1);
var function_name = '';
if (Array.isArray(object)) {
function_name = function_names[1];
}
else {
function_name = function_names[0];
}
var time_before_retrieval = new Date().getTime();
window[function_name](object, random_id);
var time_after_retrieval = new Date().getTime();
return get_diff_in_seconds_from_two_milisecond_values(time_before_retrieval,
time_after_retrieval);
}
function test_retrieval_times(number_of_users, tests_num, object_type) {
var users_dict = generate_users_dict(number_of_users);
var users_list = generate_users_list_from_dict(users_dict);
var times_array = [];
var object = '';
if (object_type == 'dict') {
object = users_dict;
}
else {
object = users_list;
}
for (var i = 0; i < tests_num; i++) {
times_array.push(get_time_for_retrieval_of_item_from_object(object,
number_of_users));
}
return times_array;
}
function get_average_retrieval_time(object_type, number_of_users,
numbers_of_retrievals) {
var retrieval_times = test_retrieval_times(number_of_users, numbers_of_retrievals,
object_type);
var sum = 0;
for (var i = 0; i < retrieval_times.length; i++) {
sum += retrieval_times[i];
}
console.log('average retrieval time for ' + object_type + ': ' + sum /
numbers_of_retrievals);
}
var number_of_users = parseInt(prompt("Please enter object size", "1000000"));
var number_of_retrievals = parseInt(prompt("Please enter number of retrievals",
"100"));
get_average_retrieval_time('dict', number_of_users, number_of_retrievals);
get_average_retrieval_time('list', number_of_users, number_of_retrievals);
The results of the tests are printed to the console.

Sorting an array of object

I wish to sort an array of medals. My first sort returns an array sorted according to the gold medals. I then wish to range those which are having the same gold but silver medals are different (same for bronze). I use the following codes that actually makes me run out of memory. This is my code:
static sort(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
}
You'll want to improve your compare function so it takes care of that requirement:
data.sort((a, b) => (b.medal.gold - a.medal.gold)
|| (b.medal.silver - a.medal.silver)
|| (b.medal.bronze - a.medal.bronze) )
And then you don't need the (endless) for loop at all.
You have to set next to null somewhere, because it keeps the value from the previous iteration and the if(next) is always true. Afterwards the function will always create one more element and add it in the array (sorted[i+1] = sorted[i]) until you run out of memory.
Here is a working example:
var rawData =
[{ id: 1, medal: {gold: 2, silver: 1, bronze: 1}},
{ id: 2, medal: {gold: 2, silver: 1, bronze: 2} },
{ id: 3, medal: {gold: 5, silver: 1, bronze: 4} } ];
function sortData(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
next = undefined;
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
};
console.log(sortData(rawData))
Please note that in the function you are using medal instead of medals as the data you have provided in one of your comments.

Target specific attributes with merge sort

Implemented the merge sort algorithm in my javascript code.
I'm wonder how I can target specific attributes like date, title, name etc for sorting in an array when calling merge sort like mergeSort(array);.
function mergeSort(arr){
var len = arr.length;
if(len <2)
return arr;
var mid = Math.floor(len/2),
left = arr.slice(0,mid),
right =arr.slice(mid);
return merge(mergeSort(left),mergeSort(right));
}
function merge(left, right){
var result = [],
lLen = left.length,
rLen = right.length,
l = 0,
r = 0;
while(l < lLen && r < rLen){
if(left[l] < right[r]){
result.push(left[l++]);
}
else{
result.push(right[r++]);
}
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
Using it in a sort options method. What I want is to print a sorted list. The way the list is sorted will be defined by the users chosen sort option.
function sortConfig(array, sortOption){
if(sortOption == 'title') mergeSort(array.Title);
//..etc
}
To implement the behavior with an optional argument, you could do it in the following way:
function mergeSort(arr, compare = (item => item))
This would set compare function to be the item itself when running the merge
and then we update the calling of the merge and mergeSort itself, where they now all get the compare argument
return merge(mergeSort(left, compare), mergeSort(right, compare), compare);
and ofcourse the declaration for your merge function itself
function merge(left, right, compare)
Which then calls the compare function upon comparison, like here:
if (compare(left[l]) < compare(right[r]))
This lets you choose wether you wish to give an argument or not wen you call your mergeSort function, like:
console.log(mergeSort(nrs).join(','));
console.log(mergeSort(nrs, n => -n).join(','));
console.log(mergeSort(arr, i => i.id));
console.log(mergeSort(arr, i => i.title));
function mergeSort(arr, compare = (item => item)) {
var len = arr.length;
if (len < 2)
return arr;
var mid = Math.floor(len / 2),
left = arr.slice(0, mid),
right = arr.slice(mid);
return merge(mergeSort(left, compare), mergeSort(right, compare), compare);
}
function merge(left, right, compare) {
var result = [],
lLen = left.length,
rLen = right.length,
l = 0,
r = 0;
while (l < lLen && r < rLen) {
if (compare(left[l]) < compare(right[r])) {
result.push(left[l++]);
} else {
result.push(right[r++]);
}
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
var arr = [{
title: 'test 5',
id: 4
}, {
title: 'test',
id: 0
}, {
title: 'test 3',
id: 2
}, {
title: 'test 4',
id: 3
}];
var nrs = [5, 3, 7, 156, 15, 6, 17, 9];
// and call like
console.log(mergeSort(nrs).join(','));
console.log(mergeSort(nrs, n => -n).join(','));
// or like
console.log(mergeSort(arr, i => i.id));
console.log(mergeSort(arr, i => i.title));
For the sake of brevity, these examples show how to sort an array of objects based on a property with a string value. You would most likely need to create some additional logic to handle different types of properties.
1. Array.sort()
You can do this with the Array.sort() method
Fiddle Example
myThings = [
{ alpha: 'a' },
{ alpha: 'x' },
{ alpha: 'p' },
{ alpha: 'orange' },
{ alpha: 'c' },
{ alpha: 'w' }
];
myThings.sort(function(a, b) {
var alphaA = a.alpha.toUpperCase();
var alphaB = b.alpha.toUpperCase();
if (alphaA < alphaB) return -1;
if (alphaA > alphaB) return 1;
return 0;
});
console.log(myThings);
2. Or, compare array item property value instead of array item value
Fiddle Example
function mergeSort(arr, prop) {
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left, prop), mergeSort(right, prop), prop);
}
function merge(left, right, prop) {
var result = [];
while (left.length && right.length) {
if (left[0][prop] <= right[0][prop]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
myThings = [
{ alpha: 'a' },
{ alpha: 'x' },
{ alpha: 'p' },
{ alpha: 'orange' },
{ alpha: 'c' },
{ alpha: 'w' }
];
console.log(mergeSort(myThings, 'alpha'));

How to reassign a list (in typescript) based on a "rank" object property?

I am trying to make a function to reassign a list based on their rank property.
For example:(my object has other property)
var array=[
{id:1,rank:2},
{id:18,rank:1},
{id:53,rank:3},
{id:3,rank:5},
{id:19,rank:4},//this item
]
This item {id:19,rank:4} is now in 2d position. The array becomes
item= { currentRank: 4; newRank: 2} //see below
array=[
{id:1,rank:3},
{id:18,rank:1},
{id:53,rank:4},
{id:3,rank:5},
{id:19,rank:2},
]
FYI : These items are re-order after a html drag&drop operation.
So I am trying to make a function to re-assign ranks based on the droped item rank.
I know the drop item new rank and its old rank.
So far I have done the following but it is not working for all cases:
public reorderArray(item: { currentRank: string; newRank: string }, array: { id: string, rank: string }[]): { id: string, rank: string } [] {
let arr = array.map(a => Object.assign({}, a)).sort((a, b) => (parseInt(a.rank) - parseInt(b.rank))).slice();
//To avoid to change the reference??
let isOrdered = arr.every((element, index, array) => {
return array[index + 1] ? element.rank + 1 == array[index + 1].rank : true
});
if (isOrdered && arr[0].rank == (1).toString()) {
if (parseInt(item.currentRank) < parseInt(item.newRank)) {
//on descend un élément dans la liste => +1 entre le currentRank et )le newRank
for (let i = parseInt(item.currentRank); i < parseInt(item.newRank); i++) {
arr[i].rank = (parseInt(arr[i].rank) - 1).toString();
}
arr[parseInt(item.currentRank)].rank = (parseInt(item.newRank)).toString();
}
else if (parseInt(item.currentRank) > parseInt(item.newRank)) {
for (let i = parseInt(item.newRank); i < parseInt(item.currentRank); i++) {
arr[i].rank = (parseInt(arr[i].rank) + 1).toString();
}
arr[parseInt(item.currentRank)].rank = (parseInt(item.newRank) + 1).toString();
}
return arr
}
else {
alert("This list is not ordered");
}
}
nb: if array is not properly oredered (rank is 1,3,4...), function doesn't do anything.
You could use an array for splicing and iterate then for the correction of the range.
function changeRank(object) {
ranks.splice(object.newRank - 1, 0, ranks.splice(object.currentRank - 1, 1)[0]);
ranks.forEach(function (a, i) {
a.rank = i + 1;
});
}
var array = [{ id: 1, rank: 2 }, { id: 18, rank: 1 }, { id: 53, rank: 3 }, { id: 3, rank: 5 }, { id: 19, rank: 4 }],
ranks = [];
array.forEach(a => ranks[a.rank - 1] = a);
console.log(array);
changeRank({ currentRank: 4, newRank: 2 });
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think you might be approaching this incorrectly.
Why not loop through all of the items and then if the rank is equal too or great then the current one increase it's rank? Then once you're done set the rank for the updated item:
Something like this:
for(var x = 0; x < items.length; x++){
if(items[x].rank >= item.newRank && items[x].rank <= item.currentRank){
items[x].rank++;
}
}
item.rank = item.newRank;
This logic must work. I've done it with the concept of array. Consider array index as rank.
if (new_rank < current_rank)
{
item = arr[current_rank]
i = new_rank;
temp = arr[i];
i++;
while(i<current_rank)
{
temp1 = arr[i];
arr[i] = temp;
temp = temp1;
i++;
}
arr[new_rank] = item;
}
else
{
item = arr[current_rank]
i = new_rank;
temp = arr[i];
i--;
while(i>current_rank)
{
temp1 = arr[i];
arr[i] = temp;
temp = temp1;
i--;
}
arr[new_rank] = item;
}

Categories