Given this Object in Javascript
{
"0": "hello",
"1": { text: "world" }
}
What's the shortest way to create this array from that object?
[
"hello",
{ text: "world" }
]
I've seen somebody using Array.prototype.splice(theObject) but it doesn't seem to work for me.
Update:
Order of output needs to be guaranteed
0 and 1 can be any arbitrary string value and the order at property level needs to be maintained in the corresponding array.
Needs to work with Node.js 6
Just use Object.values:
console.log(Object.values({
"0": "hello",
"1": { text: "world" }
}));
If you want to be sure that the original keys of the object correspond to the positions in the resulting array, you can't rely on Object.values() or a for ... in loop. You can use Object.keys() and sort that array.
var keys = Object.keys(yourObject);
keys.sort();
var array = keys.map(key => yourObject[key]);
Now understand that the call to .sort() can include a comparator function to impose any ordering desired on the original object keys. The sample in the OP is very simple, and the above would work. However more complicated keys might require a custom comparator.
This should maintain the order based on the keys, including where keys have more than one digit
const test = {
"0": "hello",
"3": "three",
"1": { text: "world" },
"2": "two",
"11": "eleven",
}
const transform = obj => Object.keys(obj)
.sort((a, b) => parseInt(a) > parseInt(b) ? 1 : -1)
.map(key => obj[key])
console.dir(transform(test))
let src = {
"0": "hello",
"1": {
text: "world"
}
}
let res = [src].map(it => [it['0'], it['1']])
console.log(res)
Try using for..in loop like this:
let obj = {
"0": "hello",
"1": { text: "world" }
}
let result = []
for(var value in obj){
result.push(obj[value])
}
Related
My usage will contain 6 different object types (some which contain double nested arrays), and any possibility of number of entries, on the condition that an given entry is unique.
These objects do not have a consistent unique identifier (a unique identifier is applied in backend on submission).
here is an example of what the array may look like (only 2 object types):
arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{timeline: "250", chars[{a},{b},{c}]},
{timeline: "220", chars[{d},{e},{f}]},
]
obj = {name:"aaa",time:22}
My intention is to gain a true or false based on if obj is inside arr
I have tried methods:
I was suggested this method & it errors: #<Object> is not a function
console.log(arr.find(obj))
I also found this suggestion but it will always return false even with the element present
console.log(arr.includes(object))
I tried this method myself, though it will always fail.
console.log(arr.filter((element, index) => element === obj)
With attempt 4, If I was to compare name, this would be insufficient as unique time would be ignored missing valid entries.
If I was to pass every field, this would also not work as each object may or may not have the field and cause error.
Its not really possible to manually pre-filter filter into distinct categories, as every time a new type is added it will need manually adding to the filter.
If there is a library which could do this that you know of, please let me know as that would be perfect. Otherwise any other suggestions (excluding separating arrays) Would be greatly appreciated.
Use arr.some() to check if the required object is present in the array.
To compare the objects, a simpler way is to Stringify both the Objects and compare them.
const arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{name: "aaa", chars: ["a", "b", "c"]},
{name: "bbb", chars: ["d", "e", "f"]},
]
const obj1 = {name:"aaa", time: 15}
const obj2 = {name:"aaa",chars: ["a", "b", "c"]}
console.log(arr.some((element) => JSON.stringify(element) === JSON.stringify(obj1))) // true
console.log(arr.some((element) => JSON.stringify(element) === JSON.stringify(obj2))) // true
Didn't give much thought on performance.
I didn't put much thought on performace here but this might help:
function checkObjectInArray(arr, obj) {
const res = arr.some((el) => deepEqual(el, obj));
console.log(res);
}
function deepEqual(obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
for (let prop in obj1) {
if (!obj2.hasOwnProperty(prop) || obj2[prop] !== obj1[prop]) {
return false;
}
}
return true;
}
in your case you can use it like:
arr = [
{ name: "aaa", time: 15 },
{ name: "aaa", time: 22 },
{ timeline: "250", data: ["2", "3", "4"] },
{ timeline: "251", data: ["2", "3", "4"] }, // what is chars[{d},{e},{f}] ?!
];
obj = { name: "aaa", time: 22 };
checkObjectInArray(arr, obj);
Observation : arr is not a valid array. Nested chars is not containing a valid value.
Solution : You can simply achieve the requirement by Just converting the JSON object into a JSON string and by comparing.
This solution works fine as you are just trying to find a single object in the passed arr.
Live Demo :
const arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{timeline: "250", chars: [{a: 1},{b: 2},{c: 3}]},
{timeline: "220", chars: [{d: 4},{e: 5},{f: 6}]},
];
const obj = {name:"aaa",time:22};
const res = JSON.stringify(arr).indexOf(JSON.stringify(obj)) !== -1 ? true : false;
console.log(res);
I have an array containing objects that every element but the last one are objects, but I want to convert them into an array of arrays and add the last element.
To be more explicit here is how I have it:
[
{ 0: [1,2], 1: [6,2], name: "" },
{ 0: [3,4], 1: [2,2], name: "" }
]
and the result I want is this one:
[
{ multipolygon: [ [1,2], [6,2] ], name: ""},
{ multipolygon: [ [3,4], [2,2] ], name: ""}
]
Each single array contained inside the original array is converted into an array of those arrays.
I have tried doing this:
const zonesArray = zones.map(el => Object.values(el)) // obj => array
const polygons = zonesArray.filter(el => el.pop()) // array without name
to get all the arrays contained inside the obj but then I realized how can I replace this into the original objects.
I have tried to modify the groupBy function found on MDN:
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
let key = obj[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
But I can't seem to find the answer
It doesn't look like you're trying to group by a property, you're trying to transform each object in the array separately - which can be done by taking the name and the numeric properties together when mapping, then returning the shape of the new object:
const arr = [
{ 0: [1,2], 1: [6,2], name: "" },
{ 0: [3,4], 1: [2,2], name: "" }
];
const result = arr.map(({ name, ...rest }) => ({
name,
multipolygon: Object.values(rest)
}));
console.log(result);
I am using ReactJS. I have an object which looks like this:
{
autumn=firstContactPersonName: "John",
autumn=firstContactPersonPhone: "46442644",
autumn=secondContactPersonName: "Jhonny",
autumn=secondContactPersonPhone: "46442644",
christmas=firstContactPersonName: "Tommy",
christmas=firstContactPersonPhone: "46442644",
christmas=secondContactPersonPhone: "Thomas",
winter=firstContactPersonPhone: "46442644",
winter=firstContactPersonName: "Peter",
winter=secondContactPersonName: "Tom",
winter=secondContactPersonPhone: "44664455"
}
How can I group them based on autumn, christmas, winter. So one object with autumn have the autumn fields, the second one christmas have the christmas field, and the third one winter have the winter fields?
And so on. All fields will have a segment name before =, so I can easily see what kind of segment they belong to.
This is achievable with pure JS, no need for lodash (which I'm not familiar with).
This function takes every property of your input object, splits the key around the equals sign for the season part (prefix) and rest (appendix). Afterwards it looks for an object in the output array with the same season and adds the properties to it. If there is no object with specified season, it creates a new one.
function processData(inputObj) {
var data = [];
for (var key in inputObj) {
var prefix = key.split("=")[0];
var appendix = key.split("=")[1];
var dataObject = data.find(o => o.season == prefix);
if (!dataObject) {
dataObject = {season: prefix};
data.push(dataObject);
}
dataObject[appendix] = inputObj[key];
}
return { data };
}
produces this output:
{
"data": [
{
"season": "autumn",
"firstContactPersonName": "John",
"firstContactPersonPhone": "46442644",
"secondContactPersonName": "Jhonny",
"secondContactPersonPhone": "46442644"
},
{
"season": "christmas",
"firstContactPersonName": "Tommy",
"firstContactPersonPhone": "46442644",
"secondContactPersonPhone": "Thomas"
},
{
"season": "winter",
"firstContactPersonPhone": "46442644",
"firstContactPersonName": "Peter",
"secondContactPersonName": "Tom",
"secondContactPersonPhone": "44664455"
}
]
}
You can produce your groups by looping over the keys in the object. Split the key on = and capture the results. The first element should be the group, the second the property to access within this group.
Create a new object if a group doesn't yet exist. Then set the property within the group equal to the value of the original object.
const input = {
"autumn=firstContactPersonName": "John",
"autumn=firstContactPersonPhone": "46442644",
"autumn=secondContactPersonName": "Jhonny",
"autumn=secondContactPersonPhone": "46442644",
"christmas=firstContactPersonName": "Tommy",
"christmas=firstContactPersonPhone": "46442644",
"christmas=secondContactPersonPhone": "Thomas",
"winter=firstContactPersonPhone": "46442644",
"winter=firstContactPersonName": "Peter",
"winter=secondContactPersonName": "Tom",
"winter=secondContactPersonPhone": "44664455"
};
const output = {};
for (const key in input) {
const [season, property] = key.split("=", 2);
if (!output[season]) output[season] = {};
output[season][property] = input[key];
}
console.log(output);
I find my explanation a bit confusion, but I hope that combined with the code snippet you understand what I'm saying.
Use Object.entries() to generate an array of [key, value] pairs, and iterate with Array.reduce(). For each pair, split the key by = to get the group key, and the value key, create the group's object if none exists, and assign the value to the object.
const data = {"autumn=firstContactPersonName":"John","autumn=firstContactPersonPhone":"46442644","autumn=secondContactPersonName":"Jhonny","autumn=secondContactPersonPhone":"46442644","christmas=firstContactPersonName":"Tommy","christmas=firstContactPersonPhone":"46442644","christmas=secondContactPersonPhone":"Thomas","winter=firstContactPersonPhone":"46442644","winter=firstContactPersonName":"Peter","winter=secondContactPersonName":"Tom","winter=secondContactPersonPhone":"44664455"}
const result = Object.entries(data)
.reduce((r, [k, v]) => {
const [gKey, key] = k.split('=') // get the key
r[gKey] = r[gKey] || {} // generate the object if it doesn't exist
r[gKey][key] = v // assign the value to the object
return r
}, {})
console.log(result);
In previous question I tried to group arrays by parent ids and then remove them from each object - Group arrays by parent ids object Ramda.
But now I have a new issue. For example I want to update title in object with id 12.
My data model:
const stuff = {
"31": [
{
"id": "11",
"title": "ramda heeeelp"
},
{
"id": "12",
"title": "ramda 123"
}
],
"33": [
{
"id": "3",
"title": "..."
}
],
"4321": [
{
"id": "1",
"title": "hello world"
}
]
}
Attempts:
const alter = (id, key, value) => pipe(
values,
flatten,
update(...) // <= end of my attempts
// then group again
)
alter('12', 'title', 'new heading 123')(stuff)
You can use lenses here:
Over: https://ramdajs.com/docs/#over
Set: https://ramdajs.com/docs/#set
const titleLens = R.curry((key, id, data) => R.lensPath([
key,
R.findIndex(R.whereEq({ id }), R.propOr([], key, data)),
'title'
]));
// ----
const stuff = {
"31": [
{
"id": "11",
"title": "ramda heeeelp"
},
{
"id": "12",
"title": "ramda 123"
}
],
"33": [
{
"id": "3",
"title": "..."
}
],
"4321": [
{
"id": "1",
"title": "hello world"
}
]
}
const title3111 = titleLens('31', '11', stuff);
const result = R.set(title3111, 'DID RAMDA HELP YOU?', stuff);
console.log('result', result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
I think this would be best done with a custom lens. Here I write a lens creator (idInObjLens) that focuses on the object with the correct id, regardless of what group it falls in. Then I write myLens to accept an id and a property name and return you a lens that focuses on that property of the object.
With those, we can use Ramda's view, set, and over to see the value, set the value, or update the value with a function:
const idInObjLens = (id) => lens (
(obj) => {
let index = -1
const child = find (value => (index = findIndex (propEq ('id', id), value)) > -1, values (obj) )
if (child) return child [index]
},
(val, obj) => {
let index = -1
const [key, value] = find (([key, value]) => (index = findIndex (propEq ('id', id), value)) > -1, toPairs (obj) )
return assoc (key, update (index, val, value), obj)
},
)
const myLens = (id, key) => compose (idInObjLens (id), lensProp (key))
const stuff = {"31": [{"id": "11", "title": "ramda heeeelp"}, {"id": "12", "title": "ramda 123"}], "33": [{"id": "3", "title": "..."}], "4321": [{"id": "1", "title": "hello world"}]};
[
view (myLens ('12', 'title'), stuff),
set (myLens ('3', 'title'), 'new heading 123', stuff),
over (myLens ('1', 'title'), toUpper, stuff),
] .forEach (x => console .log (x))
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lens, find, findIndex, propEq, values, toPairs, assoc, update, compose, lensProp, view, set, over, toUpper } = R</script>
Note that the set and over will only work if that id actually exists. You might want to check using view first.
myLens is simple; it's just how lens composition works. (Notice that it seems to flow backward from regular compose; the technical reasons are interesting, but beyond the scope of an SO answer.) But idInObjLens is more complex. As with all lenses, it takes a getter and a setter. Both of them simultaneously find the object key that contains the item with the id and the index of that key in the array associated with that object key. The getter simply returns the value. The setter uses assoc to update the outer object and update to update the array inside it. All other nested objects are simply returned by reference.
This is not code to be proud of. It works, and of course that's the main thing. But I really don't like calculating the array index as a side-effect of the find call. Yet calculating it a second time just seems overkill. I also don't really like the name idInObjLens and I always feel that if I don't have a good name, I'm missing something fundamental. (I don't have the same objection to myLens, as I assume you will have a better name for this for your use-case.)
The big difference between this and the solution from Hitmand is that this lens does not require you to know up-front which key in the outer object holds the item with your id. That adds a fair bit of complexity to the solution, but makes its API much more flexible.
You map all array inside properties, and use R.when to evolve all objects with matching ids, and replace the property's (title in your case) value:
const { curry, map, when, propEq, evolve, always } = R
const fn = curry((id, prop, content) =>
map(map( // map all objects of all properties
when(
propEq('id', id), // if the id of an object matches
evolve({ [prop]: always(content) })) // evolve it's property to the content
)
))
const data = {"31":[{"id":"11","title":"ramda heeeelp"},{"id":"12","title":"ramda 123"}],"33":[{"id":"3","title":"..."}],"4321":[{"id":"1","title":"hello world"}]}
const result = fn('12', 'title', 'new heading 123')(data)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
Please check this fiddle example
How can I sort an orderedMap object by the keys sequence in a descending order? I know there is a sortBy method but the document doesn't give a clearer example.
Here's my original object:
var props = {
"item": {
"A": {
"sequence": 5
},
"B": {
"sequence": null
},
"C":{
"sequence": 2
}
}
}
I'd like the result to be like:
var props = {
"item": {
"A": {
"sequence": 5
},
"C":{
"sequence": 2
},
"B": {
"sequence": null
}
}
}
Example Code:
var data = Immutable.fromJS(props,(key, value)=>{
var isIndexed = Immutable.Iterable.isIndexed(value);
return isIndexed ? value.toList() :value.toOrderedMap();
});
var sorted = data.get('item').sortBy((item) => item.get('sequence'));
console.log(sorted.toJS())
The function you are providing to sortBy() is comparator and it needs to return a Number. By adding a minus in front of the item.get('sequence') or following the result with reverse() you will reverse the sort, getting you the output that you want.
Here's an example:
var sorted = data.get('item').sortBy((item) => -item.get('sequence'));
// or var sorted = data.get('item').sortBy((item) => item.get('sequence')).reverse();
// you can now use .map() to go through your OrderedMap which has been
// sortbed by the sequence property and would look like your desired output
Note that you are working with an OrderedMap and that simply calling sorted.toJS() would return a regular JS object where the keys are, obviously, not sorted.
// If you need to iterate through the map and return some
// plain JS you can do something like:
sorted.map(x => x.get('sequence')).toJS()
// => Object {A: 5, C: 2, B: null}