Numbers from txt file aren't sorted - javascript

If I use [3, 8, -10, 23, 19, -4, -14, 27].sort((a,b)=>a-b) it works.
But if I use...
leaderboard = (fs.readFileSync('leaderboard.txt', 'utf-8'))
[leaderboard].sort((a,b)=>a-b)
...it just sends the numbers unsorted.
leaderboard.txt = 3, 8, -10, 23, 19, -4, -14, 27

The leaderboard is just a string when it's read from a file.
Assuming that the file contains '3, 8, -10, ...', you should first split the string to an array. then cast item(with type string) to number and then sort.
const sortedLeaderBoard = leaderboard
.split(', ')
.map(Number)
.sort((a, b) => a - b);
console.log(sortedLeaderBoard);
is my assumption correct?

Related

sort and filter an object array with lodash (or vanilla javascript)

I have this object I want to sort and filter by retaining only the 2 highest values by object.
obj={ A :[{
asset: 9,
biodiversity: 4,
infrastructure: 15,
deflation: 11,
energy: 9
}],
B:[{
asset: 12,
biodiversity: 10,
infrastructure: 9,
deflation: 7,
energy: 15
}],
C:[{
asset: 2,
biodiversity: 12,
infrastructure: 6,
deflation: 6,
energy: 8
}]}
I would like to sort the objects by their values and filter out the 2 highest e.g:
{A :[{
infrastructure: 15,
deflation: 11
}],
B:[{
energy: 15,
asset: 12
}],
C:[{
biodiversity: 12,
energy: 8
}]}
I have tried this for sorting:
Object.keys(obj).forEach((a) => _.sortBy(obj[a][0])))
But that is wrong obviously.
I am using lodash but will accept vanilla javascript solution as well.
You could get the entries of the inner objects and sort by value descending, get the top two key/value pairs and build a new object from it.
const
data = { A: [{ asset: 9, biodiversity: 4, infrastructure: 15, deflation: 11, energy: 9 }], B: [{ asset: 12, biodiversity: 10, infrastructure: 9, deflation: 7, nergy: 15 }], C: [{ asset: 2, biodiversity: 12, infrastructure: 6, deflation: 6, energy: 8 }]},
result = Object.fromEntries(Object
.entries(data)
.map(([k, a]) => [k, a.map(o => Object.fromEntries(Object
.entries(o)
.sort((a, b) => b[1] - a[1])
.slice(0, 2)
))])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
(Edit/Note: This is based on the code you originally posted. I'm glad to see you updated your question and got rid of the wrapping array.)
Here's a relatively functional approach.
The secondLargestValue function finds the threshold value within each object.
The copyWithoutValsBelowThreshold function gives us a modified copy of the object.
We loop though the entries in the top-level object and apply these two functions.
See comments in the code for further clarification.
let json = getArray(); // Identifies the original array
// Defines `secondLargestVal` function
const secondLargestVal = someObj =>
// Gets second item of array after sorting numerically in descending order
Object.values(someObj).sort((a, b) => b - a)[1];
// Defines `copyWithoutValsBelowThreshold` function
const copyWithoutValsBelowThreshold = ( (someObj, threshold) => {
// This function doesn't mutate the original value
clone = Object.assign({}, someObj); // Copies obj
for(let prop in clone){
// Loops through properties, deleting non-qualifying ones
if(clone[prop] < threshold){
delete clone[prop];
}
}
return clone;
});
// Defines our main function
const mutateJson = () => {
let entries = Object.entries(json[0]);
entries = entries.map(entry => {
// `entry[0]` is the property name (eg: 'A') -- we don't actually use this
// `entry[1]` is the value (in this case, an array containing a single object)
let obj = entry[1][0]; // Identifies the actual object
const threshold = secondLargestVal(obj); // Identifies minimum value
// Updates the entry, deleting properties whose values are too low
entry[1][0] = copyWithoutValsBelowThreshold(obj, threshold);
return entry;
});
json[0] = Object.fromEntries(entries); // Replaces the top-level object
}
// Calls the main function
mutateJson();
console.log(json);
// Provides the original array
function getArray(){
return [{ A :[{
asset: 9,
biodiversity: 4,
infrastructure: 15,
deflation: 11,
energy: 9
}],
B:[{
asset: 12,
biodiversity: 10,
infrastructure: 9,
deflation: 7,
energy: 15
}],
C:[{
asset: 2,
biodiversity: 12,
infrastructure: 6,
deflation: 6,
energy: 8
}]}]
}

what is the difference between filtering on accumulator/current in reduce method

When chaining the Array.prototype.reduce with Array.prototype.filter what is the difference (conceptually and under the hood) when filtering on the current value instead of the accumulator value?
// function union creates a union of all values that appear among arrays
// example A
const union = (...arrays) => {
return arrays.reduce((acc, curr) => {
const newElements = acc.filter(el => !curr.includes(el));
return curr.concat(newElements);
});
};
console.log(union([1, 10, 15, 20], [5, 88, 1, 7], [1, 10, 15, 5]));
// output (7) [1, 10, 15, 5, 88, 7, 20]
// example B
const union = (...arrays) => {
return arrays.reduce((acc, curr) => {
const newElements = curr.filter(el => !acc.includes(el));
return acc.concat(newElements);
});
};
console.log(union([1, 10, 15, 20], [5, 88, 1, 7], [1, 10, 15, 5]));
//output (7) [1, 10, 15, 20, 5, 88, 7]
The difference in output would suggest that the order in which the arrays are being evaluated is 'opposite'. As far as I can tell when using arr.filter the values are evaluated from end to beginning with the opposite being true for curr.filter . Besides from that are they any other consequences dependent on if you filter through the accumulator or current value? Could this throw an error in a different context?
The issue isn't about the use of filter inside of reduce, so much as it is about the order in which you're using acc and curr.
When I'm running into seemingly strange inconsistencies like this, the first step I usually take is to create a test case and run through it manually. Here, you've already created a test case for us...
const testData = [
[1, 10, 15, 20],
[5, 88, 1, 7],
[1, 10, 15, 5],
]
Now we need to run through each version of the function and see what the output is at each stage.
One thing to note (which I didn't know until this evening!) is that if reduce doesn't receive an initialValue as the second argument, it will use the first item in the array as the initialValue. This means we only need to consider 2 executions of each function instead of 3. 😅
Example A
const union = (...arrays) => {
return arrays.reduce((acc, curr) => {
const newElements = acc.filter(el => !curr.includes(el))
return curr.concat(newElements)
})
}
In the first version of the function, the short description of what's happening is that we're looping over the accumulator (acc) and removing all items that already exist in the array that we're currently comparing (curr). Then we add that list to the end of curr.
The fact that we’re pushing newElements onto the end of curr is important. This is why the order is different for the 2 different versions.
First execution
const acc = [1, 10, 15, 20]
const curr = [5, 88, 1, 7]
const newElements = [10, 15, 20] // these elements exist in acc but not in curr
curr.concat(newElements) === [5, 88, 1, 7, 10, 15, 20]
Second execution
const acc = [5, 88, 1, 7, 10, 15, 20] // carried over from first execution
const curr = [1, 10, 15, 5]
const newElements = [88, 7, 20] // these elements exist in acc but not in curr
curr.concat(newElements) === [1, 10, 15, 5, 88, 7, 20]
Example B
const union = (...arrays) => {
return arrays.reduce((acc, curr) => {
const newElements = curr.filter(el => !acc.includes(el))
return acc.concat(newElements)
})
}
In the first version of the function, the short description of what's happening is that we're looping over the array that we’re currently comparing (curr) and removing all items that already exist in the accumulator (acc). Then we add that list to the end of acc.
You can already see at the end of the first execution below that the results are turning out in a much different order.
First execution
const acc = [1, 10, 15, 20]
const curr = [5, 88, 1, 7]
const newElements = [5, 88, 7] // these elements exist in curr but not in acc
acc.concat(newElements) === [1, 10, 15, 20, 5, 88, 7]
Second execution
const acc = [1, 10, 15, 20, 5, 88, 7] // carried over from first execution
const curr = [1, 10, 15, 5]
const newElements = [] // these elements exist in acc but not in curr
acc.concat(newElements) === [1, 10, 15, 20, 5, 88, 7]
Conclusion
The short answer to your question is that the difference between filtering on the accumulator and the current array is that the results are going to be different so long as the inputs are different. 🤷🏻‍♂️
Besides from that are they any other consequences dependent on if you filter through the accumulator or current value? Could this throw an error in a different context?
Fortunately, there’s not any concern about errors. It is notable, however, that the second version of your function is ~10% faster than the first version. I’d guess that this is purely circumstantial. A different test data set may produce different performance results.
In example 1, by the time you concat the two lists, you make sure that the accumulator won't contain any element from current.
In example 2, on the other hand, you make sure that current won't contain any element that is already present in accumulator.
The difference is on the final order in which the elements will appear
I think both examples are not efficient since they both involve O(n2) time complexity, since you are nesting iterations. The second one, as stated by others, might be a little more performant since the nested iterations would be made on a chunk that is presumably shorter than the accumulator.
I'd rather write more or less like this:
const union = (...tuples) => Array.from(
new Set(
tuples.flatMap(n => n),
)
);
console.log(
union([1, 10, 15, 20], [5, 88, 1, 7], [1, 10, 15, 5]),
);

Why doesn't JS complier recognize this call to Array.prototype.filter

I am trying to figure out why my call to .prototype.filter is giving me a TypeError: curr.filter is not a function.
const intersection = (arrays) => {
return arrays.reduce((acc, curr) => {
return curr.filter(el => acc.includes(el));
});
};
console.log(intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]));
To my understanding I am declaring a function const intersection which takes in arrays and then returns the result of calling arrays.reduce which 'reduces' the results of filtering the current value and creating a new array that includes all instances of accumulator acc including the current value curr.
Since filter creates a new array on runtime I figured this would work as is yet it does not. What am I not seeing?
Use array rest parameter to get all parameter as an array. In the given code you are taking just first argument and ignoring the rest.
try this.
const intersection = (...arrays) => {
console.log("arrays: ", arrays);
return arrays.reduce((acc, curr) => {
return curr.filter(el => acc.includes(el));
});
};
console.log("Result:" , intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]));

How can I get the largest number value along with the username from this array?

Im trying to get the user & value with the highest number from this array but have had no luck in my searches. I'm starting to wonder if my array is poorly written.
{
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const highestUser = users => Object.keys(users).reduce(
(highest, current) => highest.val > users[current] ? highest : { user: current, val: users[current] },
{ user: undefined, val: -Infinity }
).user;
console.log(highestUser(users));
Use keys() and entries() methods to search your JSON object. Save largest value into e.g. const largest and then find out which key belongs to this value.
Let me try to squeeze it into a one-liner approach using Object.keys() and Array.reduce().
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const res = Object.keys(users).reduce((a, b) => users[a] > users[b] ? a : b);
console.log(res);
How the above code works is that I get the array of keys from the users object, and I use reduce to get the highest possible value and return the corresponding property from the array obtained from Object.keys().
What you show in your question is an Object, not an Array; however, it does need to be turned into an array in order to work with it.
You can do that with Object.entries(), which will return an array of all the key/value pairs in the object.
Then you can use Array.reduce() to extract the one with the largest value.
const data = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
let winner = Object.entries(data).reduce((a, b) => (a[1] > b[1]) ? a : b)
console.log(winner)

How to create a variable that is the lowest possible number?

What I have here is an algorithm that finds the highest value in each subarray and pushes it onto a separate array (final).
I want to let the variable 'value' equal to the lowest possible number so that any negative number can be considered higher than 'value'
function largestOfFour(arr) {
var final=[];
arr.map(sub => {
let value = 0; //issue
sub.map(num => {
if(num>value){value=num};
})
final.push(value)
})
return final;
}
console.log(largestOfFour([[17, 23, 25, 12], [25, 7, 34, 48], [4, -10, 18, 21], [-72, -3, -17, -10]]));
In this example the last subarray returns 0 since non of the numbers in that subarray were higher than the initial value of 'value' which is 0.
I want it to return '-3' instead since it's the highest number in the subarray.
It would appear you're simply looking for the max of each array.
Using Array#map and Math#max and spread syntax you could do something like this.
const data = [[17, 23, 25, 12], [25, 7, 34, 48], [4, -10, 18, 21], [-72, -3, -17, -10]];
const res = data.map(arr=>Math.max(...arr));
console.log(res);
You can just set value to Number.NEGATIVE_INFINITY but for what it's worth, I'd recommend simply using reduce instead of map in your inner function. That way, the inner loop will start with sub[0] as an initial value rather than depending on any placeholder.
function largestOfFour(arr) {
var final = arr.map(sub => sub.reduce((num, value) => Math.max(num, value)));
return final;
}
console.log(largestOfFour([
[17, 23, 25, 12],
[25, 7, 34, 48],
[4, -10, 18, 21],
[-72, -3, -17, -10]
]));

Categories