Sorting using nested field in ramda.js - javascript

In the documentation of sortBy, it says we can use R.prop to sort an object by it's field. But if I have to sort by a nested field, it does not work. For example R.prop('id.number') does not work.
var items = [{id:3},{id:1},{id:2}];
var sorter = R.sortBy(R.prop('id'));
sorter(items)
works fine. But if I have a nested structure
var items = [{id:{number:3}},{id:{number:1}},{id:{number:2}}];
var sorter = R.sortBy(R.prop('id.number'));
sorter(items)
returns me an empty list. I guess there is a correct way of using R.prop that I am not able to figure out.

You can use R.path for accessing nested properties, so your example would become R.sortBy(R.path(['id', 'number']))

Unless I'm mistaken, id.number itself is being checked as a property, when in fact there is only the property id. R.prop() only checks one level down - nested structures are beyond its ability, and being asked to look for the property number after doesn't work .
The documentation states that sortBy accepts a function which takes an element under consideration. The following is tested on the ramda.js REPL and works:
var items = [{id:{number:3}},{id:{number:1}},{id:{number:2}}];
var sorter = R.sortBy(function(item) {return item['id']['number'];});
sorter(items)
It works by simply looking up the properties in succession.
tl;dr Anonymous functions for the win.

Related

Javascript changes value of three objects at once

This is my code and I am trying to change value of object in packs object. But when I type it, Javascript somehow changes all three different objects, that have nothing in common. And this is the only line that changes packs, rest should stay the same, but it's all changing with this line. How?
console.log(packs[usedPack].levels[level].bestBy) //null
console.log(defaultPack.levels[level].bestBy) //null
console.log(mainPacks[usedPack].levels[level].bestBy) //null
packs[usedPack].levels[level].bestBy = nameTyped; //this changes values in three different objects
//packs[usedPack].levels[level] = nameTyped; //if I type like this, this does change only original object, rest stays the same
console.log(packs) //nameTyped
console.log(defaultPack) //nameTyped
console.log(mainPacks) //nameTyped
Edit: It was indeed problem with referencing. I was using constructor function and this function needed to deep copy complex objects. I was checking values and I used multiple loops to copy all values from objects, but it didn't work properly. I've changed all of this to JSON.parse(JSON.stringify()), and it's working now. Thanks for help.
The issue here seems like you're assigning some object by its reference.
In javascript, if you have two objects and you assign them like
a = b;
Now whenever you will change b, a will also be changed. To avoid this we do deep clone using the spread operator
// this now does not reference to b but clones it
a = {...b}
In your code, you might be assigning some objects like this. a=b
Maybe you're assigning packs, defaultPack, and mainPacks using some same object.
Updated
#David pointed out one thing and that is if you are having some complex structure (like objects within object) and then you clone it using spread operator, the inner objects will still reference the same object.
To resolve this, for easiness you can use lodash deepclone function
const clonedeep = require('lodash/clonedeep');
const deepClonedObject = clonedeep(originalObject);
This will deep clone and even if the objects are nested they won't refer to the same object.

Access Array with String key

I have two variables with JSON files. The first is a list of keys looks like this:
keylist = ["key1","key2","key3"]
The second one is generated from a database and looks like this:
data = {
"key1"{
#further data
},
"key2"{
#further data
},
"key3"{
#further data
}
}
Now I want to access the second element of the database with the key from the keylist
data.keylist[1];
Which doesn't work because the return of keylist[1] is a String? I did some research and the use of the window function was proposed. So I tried this:
window["data." + keylist[1]]();
Which leads to a "is not a function" error. What can I do to solve this problem?
As simple as that:
const mydata = data[ keylist[1] ];
Also, your code is correct from the point of syntax, but it tells completely different than you expect it to tell.
data.keylist[1];
tells JS that you expect to have an object called data which has a property called keylist and which is (most likely) type of array, and you want to get the second element of this array.
PS: And one more point here. Your question title is not completely correct because of the difference between Arrays and Object in JS.
There is no "string keys" for arrays in JS, so you cannot "access array with a string key". Well, truly speaking there are, but not for items of array. Array items only have numeric index, which you can use to access it. Objects, in contrast to arrays, may have named properties. So when you see something like that: data = myVar['data'], you can tell that you're dealing with an object, while data = someVar[0] can be both, an Array (most likely) or also an Object with key named '0'.
I don't think the issue you're having with your first example is because it returns a key. I believe the issue is because data doesn't have a property called keylist. Instead of that, try it as
data[keylist[1]]
and see if that works for you. The reason this one should work is that, in this situation, Javascript will evaluate the string return of keylist[1] and then use it as a string index for the data variable. Let me know if this works out for you :D
You can try using using something like this.
data[keylist[1]]

JavaScript object key in an x length array

I wasnt quite sure what to call this question but here is what i want to do:
I am currently creating a series geneator for chartjs that will help me create my datasets.
now the way i want to do it is by simply using object keys to extract data from each element in my array.
Each element of an array could look something like this:
as you can see this object contains other objects inside of them.
This creates a problem because say i want the name of the object feedback_skill i would have to do the following:
data.forEach(function (x) {
x['feedback_skill']['name']
});
Which cannot be hold into one variable.
Now what i could do is pass the following array: serieKey = ['feedback','name'] suggesting that the first element in the array is the first key and the next element is the variable i want to hit.
However these datasets can have an unlimited number of layers so my question to you guys is:
Is there a smart way of doing this?
I'm not aware of a native JavaScript way of doing this, but various JavaScript frameworks allow you to access deep-properties from objects like this. For example Dojo has lang.getObject and I can see that there is a JQuery plugin that does something similar, lodash as well. If you're not using these frameworks, then you could always create your own util function to perform something similar.
These types of utility function allow you to pass the target as a "dot-notation" property, so you could call:
lang.getObject("feedback_skill.name", false, x)
Using Dojo for example, but they're all much of a muchness.
I don't see any problem with your approach, unlimited number of layers can be handled in the following manner :
data.forEach(function(x){
for(i in seriesKey)
x = x[seriesKey[i]]; // x will contain whatever you wanted to retrieve when the loop ends
doSomething(x);
}
seriesKey can be an array like the one in your example, with as many elements as you need to traverse to the depth you want.

jqGrid('getGridParam','colNames') odd behavior

Using this function to return the column names of the grid works fine. The issue comes when splicing the array that it returns.
The grid includes a checkbox as the first column so I want to remove that from the array. Here is that code.
var columnTitles = $(table).getGridParam('colNames');
columnTitles.splice(0,1);
The problem comes when I use this function multiple times (it's exporting to excel). The next time I export, the getGridParam function actually returns the spliced array of column names rather than the actual ones. It's as if it's being passed by reference or something.
Further proof that it's doing that and I don't just have a problem with a global variable or something...if I do the following code:
var columnTitles = $(table).getGridParam('colNames');
var columnTitles2 = $(table).getGridParam('colNames');
columnTitles.splice(0,1);
console.log(columnTitles2);
The value of columnTitles2 comes back as the spliced array. It might be something completely stupid, but what am I missing here?
The method getGridParam returns the reference of internal parameters used by jqGrid. You should be careful if you work with arrays or objects, colNames or colNames for example. It you need to modify the arrays for your purpose, but you don't want to change the values in jqGrid you should first make copy of the arrays and then modifies the copy:
var columnTitles = $(table).jqGrid("getGridParam", "colNames").slice();
columnTitles.splice(0,1);
I used slice to make the copy of internal colNames used by jqGrid.

How can you reference a custom data structure as if it were an array? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Javascript custom index accessor
If I were to write, say, a doubly-linked list object in Javascript, is there a way to reference this new list in a fashion like an array?
For instance, if I wanted the value of node 5, I'd like to query
newLinkedList[5]
instead of doing what I've been doing, which is something like
newLinkedList.getNode(5)
Basically, is there a way to "pretty up" references to custom data structures, or do you have to do it as a custom function each time?
Arrays in Javascript are really just objects with special handling for numerical properties. You can accomplish the same thing yourself, but currently Javascript doesn't provide a simple mechanism for transparent two-way accessing of those indices; you'll have to handle adding and deleting objects with named methods, but you can still benefit from reading them with array-like indexing.
Here's an example of a simple custom "Arrayish" object:
var ll_array = {};
ll_array.length = 0;
ll_array.addNode = function (newNode) {
this[this.length] = newNode;
this.length++;
};
ll_array.addNode('Foo');
ll_array.addNode('Bar');
console.log('Length: ' + ll_array.length);
console.log(ll_array);
console.log(ll_array[0]);
​
http://jsfiddle.net/rbmsJ/1/
The brute force method of traversing the list, then assigning each item in the traversal to an array element, is the first thing that comes to mind:
Pretend you have a LinkedList object. Let's also presume there are methods for iterating the list called Head, and Next. Now, caveat being that this is entirely untested, you could create a prototype toArray() method in a manner such as:
LinkedList.prototype.toArray() = function () { var array={};
var currentItem = list.head;
while (currentItem!=null){
array.addNode(currentItem);
currentItem=currentItem.Next;
}
return array;
}
Obviously this must allow for other presumed functions, but the idea is to traverse the linked list, and put a reference to each element in an array, and return it. Hope that helps in some way.
From your question:
objects are saved in a double-linked list.
you know the getNode(int n) method to get n-th element.
there are no way to allow you get the n-th element with array style, [n]. because linked lists are not arrays, the elements in a linked list are linked to each other, this data structure is different from ordinary array or JavaScript style array:named property.
Arrays ARE double linked lists without the links. To traverse right you simply increment the index, to traverse left you decrement the index.
You could write yourself a set of functions to perform the basic list operations and still have the facility of using simple indexing to reach any aribtrary element.

Categories