Application of 'sortBy' producing unexpected results.
I've gotta be doing something stoopid. This is such a basic operation.
const input = [4,3,2,1];
const sort = list => R.sortBy(R.ascend(R.identity))(list);
console.log(sort(input)); // [ 4, 3, 2, 1 ]
I would expect the output of the 'console.log' invocation to be [ 1, 2, 3, 4 ], but it is not: the output is [ 4, 3, 2, 1 ], same as the input. What am I doing wrong?
As pointed out by Aadit M Shah in the comments you're not using sortBy correctly.
Here's quick overview of how to sort in Ramda:
sort
Returns a copy of the list, sorted according to the comparator function, which should accept two values at a time and return a negative number if the first value is smaller, a positive number if it's larger, and zero if they are equal.
One case use subtract to sort in ascending order:
sort(subtract, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
Or to sort in descending, just flip it:
sort(flip(subtract), [4, 1, 2, 3]);
//=> [4, 3, 2, 1]
sort simply expects a function that can accept two parameters which can be compared with < or >.
So how would you sort an array of strings? Strings can be compared with < or > but using subtract wouldn't make sense. This is where ascend (or descend) can be useful:
Makes an ascending comparator function out of a function that returns a value that can be compared with < and >.
sort(ascend(identity), ["b", "a", "B", "A"]);
//=> ["A", "B", "a", "b"]
And if you want to make a case insensitive comparison:
sort(ascend(toLower), ["b", "a", "B", "A"]);
//=> ["a", "A", "b", "B"]
sortBy
As we saw, sort expects that you supply it with a function that accepts two parameters that can be compared together using < or >. Numbers and strings can be compared with these operators so if you can give them to Ramda directly then:
sortBy(identity, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
is the same as:
sort(subtract, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
However as far as I can tell, sortBy will always sort things in ascending order.
sortWith
You use sortWith when you can have multiple sort criteria:
Sort by age in ascending order
Sort by name in descending order
sortWith([ascend(prop('age')), descend(prop('name'))], [
{age: 40, name: 'John'},
{age: 40, name: 'Zack'},
{age: 10, name: 'Liam'},
{age: 20, name: 'Bill'}
]);
//=> [
//=> {age: 10, name: "Liam"},
//=> {age: 20, name: "Bill"},
//=> {age: 40, name: "Zack"},
//=> {age: 40, name: "John"}
//=> ]
Related
I want to give a fixed order to an array of javascript objects and I've trying with the answer of this post but they are pointing to the value, not the keys.
fixed_order = ['group','A,'N','R']
data=[
{group: "a", A: 8, N: 6}
{group: "b", N: 4, A: 20, R: 1}
{group: "c", A: 7}
]
I've try with something like this but doesn't work.
data.sort(function (a, b) {
return fixed_order.indexOf(a.keys()) - fixed_order.indexOf(b.keys());
});
the result shoulb be something like this:
data=[
{group: "a", A: 8, N: 6}
{group: "b", A: 20, N: 4, R: 1}
{group: "c", A: 7}
]
You should not attempt to put object properties in a specific order. Objects are better thought of as unordered collections of properties. Even though modern engines implement the order defined by recent EcmaScript specifications, it is not considered good practice to make your code rely on that.
Instead, change your inner data structure from plain objects to arrays. An array is the recommended data structure when order is important.
const fixedOrder = ['group', 'A', 'N', 'R'];
const data = [
[["group", "a"], ["A", 8], ["N", 6]],
[["group", "b"], ["N", 4], ["A", 20], ["R", 1]],
[["A", 7], ["group", "c"]]
];
for (const row of data) {
row.sort((a, b) => fixedOrder.indexOf(a[0]) - fixedOrder.indexOf(b[0]));
}
console.log(data);
Output of the code:
const arr = [1, 2, 3, 4, 5];
console.log([...arr + []]);
gives
[ '1', ',', '2', ',', '3', ',', '4', ',', '5' ]
I know ...arr would return array items (like 1 2 3 4 5) and number + [] gives string, but I really got confused on why , is been added to the output array.
Is it because the ...arr in console.log()
turns out to be [..."1, 2, 3, 4, 5" + []] in which the output is the same?
Or is the some magical explanation that I am unaware of?
Here is an explanation on + operator applied to arrays. So what happens is this:
arr + [] gives you a string "1,2,3,4,5"
Then that string is spreaded/splitted (with the spread syntax) into an array of characters of that string.
Adding on Nurbol's answer when you do
const arr = [1, 2, 3, 4, 5];
console.log([...arr + []]);
It will turn out to a string with each element inside the array converting it to a string. Since the comma is also here so it will be an element of the string array.
When you do it like this
const arr = [1, 2, 3, 4, 5];
console.log((arr + []).split())
It creates the single string of that array and then you create an array of a string specifying the split point.
array.toString() adds , after each element
const a = [1, 2, 3, 4, 5]
console.log(a.toString())
// -> '1,2,3,4,5'
console.log([...a.toString()])
// -> [ '1', ',', '2', ',', '3', ',', '4', ',', '5' ]
[...aray + []] converts array to string then adds empty string and then uses [...resultString] to build result
This question already has answers here:
Javascript recursive array flattening
(27 answers)
Merge/flatten an array of arrays
(84 answers)
Closed 5 years ago.
I want to write a function that can deep flatten a given array. For example:
deepFlatten([]); // []
deepFlatten([1, 2, 3]); // [1, 2, 3]
deepFlatten([[1, 2, 3], ["a", "b", "c"], [1, 2, 3]]); // [1, 2, 3, "a", "b", "c", 1, 2, 3]
deepFlatten([[3], [4], [5]], [9], [9], [8], [[1, 2, 3]]]); // [3, 4, 5, 9, 9, 8, 1, 2, 3]
I try to solve this recursively and so far I've got this:
var deepFlatten = function (array){
var result = [];
array.forEach(function (elem) {
if (Array.isArray(elem)) {
result.concat(deepFlatten(elem)); // problem probably lies here
} else {
result.push(elem);
}
});
return result;
};
This however only pushes non-array elements to result and completely ignores the concatenating part. How can I fix this or is there a better way to write this function without the help of any external library?
You just need to set result to result = result.concat(deepFlatten(elem))
var deepFlatten = function (array){
var result = [];
array.forEach(function (elem) {
if (Array.isArray(elem)) {
result = result.concat(deepFlatten(elem)); // Fix here
} else {
result.push(elem);
}
});
return result;
};
console.log(deepFlatten([]))
console.log(deepFlatten([1, 2, 3]))
console.log(deepFlatten([[1, 2, 3], ["a", "b", "c"], [1, 2, 3]]))
console.log(deepFlatten([[[3], [4], [5]], [9], [9], [8], [[1, 2, 3]]]))
Instead you can use reduce() and spread syntax instead of concat.
var deepFlatten = function (array){
return array.reduce(function(r, e) {
return Array.isArray(e) ? r.push(...deepFlatten(e)) : r.push(e), r
}, [])
};
console.log(deepFlatten([]))
console.log(deepFlatten([1, 2, 3]))
console.log(deepFlatten([[1, 2, 3], ["a", "b", "c"], [1, 2, 3]]))
console.log(deepFlatten([[[3], [4], [5]], [9], [9], [8], [[1, 2, 3]]]))
Your code is mostly fine. .concat returns a new array, it doesn't modify the original. If you change
result.concat(deepFlatten(elem)); // problem probably lies here
to:
result = result.concat(deepFlatten(elem)); // problem probably lies here
i think it gives the correct results.
What is the best way to get the array with the max length?
I have an array as follows,
main = [
[1,2,3],
[4],
[5,6,7,8],
[9,0]
]
I need to store the array with the max length, in this case
maxArray = main[2]
Use Array#reduce method.
main = [
[1, 2, 3],
[4],
[5, 6, 7, 8],
[9, 0]
];
console.log(
main.reduce(function(a, b) {
return a.length > b.length ? a : b;
})
);
Let's say I have 3 items in a collection:
[
{
id: 'a',
items: [1, 2, 3]
}, {
id: 'b',
items: [4, 5, 6]
}, {
id: 'c',
items: [7, 8, 9]
}
]
On the JavaScript code side, all I have is an array [5, 2, 6, 4, 7, 8]. How would I compose my query to select only the 2nd object from the collection since my array has all the elements (4, 5 and 6) of its items array?
Using mongoDB Aggregation Set Operator you can filter your array. First find out intersection of given array with actual database array and after that used set equals method. check below query :
db.collectionName.aggregate({
"$project": {
"checkAllElem": {
"$setEquals": [{
"$setIntersection": ["$items", [5, 2, 6, 4, 7, 8]]
}, "$items"]
},
"items": 1
}
}, {
"$match": {
"checkAllElem": true
}
})