is there any way to reverse the object in js?
wanna make a function, but struggling hard.
I tried to find the object depth-first and then make a founded amount of iterations for .. in .. inside the object but don't know how to rewrite the new one
const initObject = {
value: 5,
next: {
value: 10,
next: {
value: 15
next: null
}
},
}
//expected result
const newObject = {
value: 15,
next: {
value: 10,
next: {
value: 5,
next: null
}
}
}
You could use a recursive function to collect all the values. Then use reduce to create a nested object from the values:
const initObject = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
}
}
const getValues = ({ value, next }) =>
next
? [value, ...getValues(next)]
: [value]
const createObject = values =>
values.reduce((next, value) => ({ value, next }), null)
const output = createObject(getValues(initObject))
console.log(output)
let newObject = null;
for(let o = initObject; o; o = o.next) {
newObject = { value: o.value, next: newObject };
}
What this is doing is looping the initialObject from the outer layer inwards (using the next property to go from layer to layer until we reach null) while constructing the newObject from the inside outwards (each constructed layer will use the previous layer, stored as the current value of newObject, as the next property, hence the initial value of newObject is null), like so:
Note: As pointed out by #jperl in a comment bellow, if the objects have multiple properties (not just value), then simply use a spread syntax to include them all in newObject by replacing:
newObject = { value: o.value, next: newObject };
with:
newObject = { ...o, next: newObject };
Demo:
const initObject = { value: 5, next: { value: 10, next: { value: 15, next: null } } };
let newObject = null;
for(let o = initObject; o; o = o.next) {
newObject = { value: o.value, next: newObject };
}
console.log(newObject);
Here's a function which does the job, the process is:
Build an array of the value values
Reverse the array (using Array.reverse)
Rebuild your object structure with the reversed array values
const reverseObject = (object) => {
const values = [];
let currentObject = object;
while (currentObject && currentObject.value) {
values.push(currentObject.value);
currentObject = currentObject.next;
}
const reversedValues = values.reverse();
const newObject = {};
let currentNewObject = newObject;
for (let i = 0; i < reversedValues.length; i++) {
currentNewObject.value = reversedValues[i];
currentNewObject.next = null;
if (i < reversedValues.length - 1) {
currentNewObject = currentNewObject.next = {};
}
}
return newObject;
};
As Bergi already mentioned in the comments, this can be done using recursion. It is even very concise and readable if you do it as follows.
It iterates through each (nested) node and builds up an inverted object using the nextAccumulator variable.
function invert({value, next}, nextAccumulator = null) {
const node = {value, next: nextAccumulator};
return next ? invert(next, node) : node;
}
const inverted = invert({
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null,
},
},
});
console.log(inverted);
You could take a single function approach with a second paramter for collecting the objects.
const
reverse = ({ value, next: sub }, next = null) => sub
? reverse(sub, { value, next })
: { value, next },
object = { value: 5, next: { value: 10, next: { value: 15, next: null } } },
result = reverse(object);
console.log(result);
You can do that in following steps:
Create a function to get all the values in form of array in order
reverse the array
convert that array back to object using recursion
const newObject = {
value: 15,
next: {
value: 10,
next: {
value: 5,
next: null
}
}
}
function reverse(obj){
let values = [];
values.reverse();
function getValue(tempObj){
values.push(tempObj.value);
if(tempObj.next){
getValue(tempObj.next);
}
}
getValue(obj)
const ob = {};
let i = 0;
function reassignValue(tempObj){
if(i >= values.length){
console.log(i)
return;
}
tempObj.value = values[i];
tempObj.next = {};
i++;
reassignValue(tempObj.next);
}
reassignValue(ob);
return ob
}
console.log(reverse(newObject))
That's a linked list. You'll easily find a reverse LinkedList algorithm on the Internet.
Note that I don't need to convert anything.
Typescript
type LinkedList = {
value: number,
next: LinkedList
} | null
const list: LinkedList = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
},
}
const reverseLinkedList = (list: LinkedList) => {
let prev = null;
let current = list
let next = null
while (current != null) {
next = current.next
current.next = prev
prev = current
current = next
}
list = prev
return list
}
console.log(reverseLinkedList(list))
Javascript
const list = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
},
}
const reverseLinkedList = (list) => {
let prev = null;
let current = list
let next = null
while (current != null) {
next = current.next
current.next = prev
prev = current
current = next
}
list = prev
return list
}
console.log(reverseLinkedList(list))
You can use recursion:
let initObject={value:5,next:{value:10,next:{value:15,next:null}}};
let flip = (obj,nexts=[]) =>{
nexts.push(obj.value);
if (obj.next){
flip(obj.next,nexts)
}
obj.value = nexts.shift()
return obj;
}
let result = flip(initObject)
console.log(result);
let original = flip(result)
console.log(original)
Related
I have an Array of objects and one object
const filterArray = [{bestTimeToVisit: 'Before 10am'}, {bestDayToVisit: Monday}]
This values are setting in a reducer and the payload will be like
{bestTimeToVisit: 'After 10am'}
or
{bestDayToVisit: Tuesday}.
So what I need is when I get a payload {bestTimeToVisit: 'After 10am'} and if bestTimeToVisit not in filterList array, then add this value to the filterList array.
And if bestTimeToVisit already in the array with different value, then replace the value of that object with same key
if(filterArray.hasOwnProperty("bestTimeToVisit")) {
filterArray["bestTimeToVisit"] = payload["bestTimeToVisit"];
} else {
filterArray.push({"bestTimeToVisit": payload["bestTimeToVisit"]});
}
I convert the object array into a regular object and then back into an object array. makes things less complicated. I'm making the assumption each object coming back only has one key/value and that order doesnt matter.
const objectArraytoObject = (arr) =>
arr.reduce((acc, item) => {
const key = [Object.keys(item)[0]];
return { ...acc, [key]: item[key] };
}, {});
const newValues = [{ someKey: 'something' }, { bestDayToVisit: 'Tuesday' }];
const filterArray = [
{ bestTimeToVisit: 'Before 10am' },
{ bestDayToVisit: 'Monday' },
];
const newValuesObj = objectArraytoObject(newValues);
const filterObj = objectArraytoObject(filterArray);
const combined = { ...filterObj, ...newValuesObj };
const combinedToArray = Object.keys(combined).map((key) => ({
[key]: combined[key],
}));
console.log(combinedToArray);
Need to iterate over the array and find objects that satisfy for modification or addition if none are found.
function checkReduced(filterrray,valueToCheck="After 10am"){
let isNotFound =true;
for(let timeItem of filterrray) {
if(timeItem.bestTimeToVisit && timeItem.bestTimeToVisit !== valueToCheck) {
timeItem.bestTimeToVisit=valueToCheck;
isNotFound=false;
break;
}
}
if(isNotFound){filterrray.push({bestTimeToVisit:valueToCheck})}
}
const filterArray = [{bestDayToVisit: "Monday"}];
checkReduced(filterArray,"After 9am");//calling the function
const updateOrAdd = (arr, newItem) => {
// get the new item key
const newItemKey = Object.keys(newItem)[0];
// get the object have the same key
const find = arr.find(item => Object.keys(item).includes(newItemKey));
if(find) { // the find object is a reference type
find[newItemKey] = newItem[newItemKey]; // update the value
} else {
arr.push(newItem); // push new item if there is no object have the same key
}
return arr;
}
// tests
updateOrAdd([{ a: 1 }], { b: 2 }) // => [{ a: 1 }, { b: 2 }]
updateOrAdd([{ a: 1 }], { a: 2 }) // => [{ a: 2 }]
Im totally clueless how to build this. I am not even sure it is even possible and I've been scratching my head for way too long now.
Lets say I have an object:
const myObj = {
simple: "test",
nested: {
obj: "alright"
}
}
Now I have found a function that lets me set a value anywhere by specifying a path in that tree. If a key is not already existing in that object, it will be created:
const set = (obj: any, path: any, val: any) => {
const keys = path.split(".");
const lastKey = keys.pop();
const lastObj = keys.reduce((obj: any, key: any) => obj[key] = obj[key] || {}, obj);
lastObj[lastKey] = val;
};
Example:
set(myObj, "nested.another.iCanEvenGoDeeper", "very deep value");
Result:
const myObj = {
simple: "test",
nested: {
obj: "alright",
another: {
iCanEvenGoDeeper: "very deep value"
}
}
}
So far so good, but now its required that I can also define a path like this to dynamically build arrays. So that I can call these:
set(myObj, "nested.myArray[0].propInsideArrayElement", "first element")
set(myObj, "nested.myArray[1].propInsideArrayElement", "second element")
that will result in an object that looks like this:
{
simple: "test",
nested: {
obj: "alright",
myArray: [
{ propInsideArrayElement: "first element" },
{ propInsideArrayElement: "second element" }
]
}
}
It needs to be recursive and work with all scenarios, but I am like I said clueless on if it is even possible. Is there by any chance some utility scripts out there that does this already? If not, can anyone point me in the right direction?
In a next step, I would like to flatten the object to have a one dimensional object again, for the last example it would then look like this:
flatten(myObj);
would then turn to
{
"simple": "test",
"nested.obj": "alright",
"nested.myArray[0].propInsideArrayElement": "first element",
"nested.myArray[1].propInsideArrayElement": "second element"
}
I have totally reworked the deepSet function now. It now supports multiple arrays and gaps in the arrays etc. I think this covers now every usecase. In the end it was way easier to figure the logic out when I started over without the reduce function
export const deepSet = (obj: any, path: string, val: any) => {
path = path.replaceAll("[", ".[");
const keys = path.split(".");
for (let i = 0; i < keys.length; i++) {
let currentKey = keys[i] as any;
let nextKey = keys[i + 1] as any;
if (currentKey.includes("[")) {
currentKey = parseInt(currentKey.substring(1, currentKey.length - 1));
}
if (nextKey && nextKey.includes("[")) {
nextKey = parseInt(nextKey.substring(1, nextKey.length - 1));
}
if (typeof nextKey !== "undefined") {
obj[currentKey] = obj[currentKey] ? obj[currentKey] : (isNaN(nextKey) ? {} : []);
} else {
obj[currentKey] = val;
}
obj = obj[currentKey];
}
};
Looks interesting :)
Here is an example for array support based on your own code.
flatten the object is also included (Using recursive calls)
const myObj = {
simple: "test",
nested: {
obj: "alright"
}
}
const getTypeVal = (currentIndex, length, val) => {
}
const set = (obj, path, val) => {
path = path.replace('[', '.[')
const keys = path.split(".");
const lastKey = keys.pop();
let lastObj = keys.reduce((obj, key, currentIndex) => {
if(key.includes('[')) {
return obj[key.substring(1, key.length-1)]
}
if(obj[key] && obj[key].length && (keys[currentIndex+1] && keys[currentIndex+1].includes('['))) {
let nextKey = keys[currentIndex+1]
nextKey = nextKey.substring(1, nextKey.length-1)
!obj[key][nextKey] && obj[key].push({})
}
return obj[key] = obj[key] || ((keys[currentIndex+1] && keys[currentIndex+1].includes('[')) ? [{}] : keys[currentIndex+1] ? {} : val)
}
, obj);
lastObj[lastKey] = val;
};
const flatternObj = (obj, result = {}, key ='') =>{
if(Array.isArray(obj)) {
obj.forEach((d,i) => {
result = flatternObj(d, result, key + `[${i}]`)
})
}
else if(typeof obj === 'object') {
for (const i of Object.keys(obj)) {
result = flatternObj(obj[i], result, key ? key + `.${i}` : `${i}`)
}
}
else {
result[key] = obj
}
return result;
}
set(myObj, "nested.myArray[0].propInsideArrayElement", "first element")
set(myObj, "nested.myArray[0].propInsideArrayElement2", "first element - 2 ")
set(myObj, "nested.myArrayTwo[0]", 'test')
set(myObj, "nested.myArray[1].propInsideArrayElement", "second element")
set(myObj, "nested.myArray[2]", 'test')
console.log(myObj)
console.log(flatternObj(myObj))
I am trying to aggregate the same key values into an array by value.
so for example I have an array of objects, like so
const data = [{foo: true},{foo: false},{bar: true},{buzz: false}]
when they get aggregated the array transforms into
[
foo: {true: [{foo: true}], false: [{foo: false}]},
bar: {true: [{bar: true}]},
buzz: {false: [{buzz: false}]}
]
the array entries is the original object.
Now I know the keys that I want to group by..
they are foo, bar, buzz and fizz.
But fizz is not part of the original array, so the return is undefined, like so
[
foo: {true:[{foo: true}], false: [{foo: false}]},
bar: {true: [{bar: true}]},
buzz: {false: A[{buzz: false}]}
fizz: {undefined: [{foo: true},{foo: false},{bar: true},{buzz: false}]}
],
how do I reduce the original array without including the fizz value that is undefined?
code here:
let v = [];
let types = ['foo', 'bar', 'buzz', 'fizz' ]
for (let x = 0; x < types.length; x++) {
let data = data.reduce((acc, i) => {
if (!acc[i[types[x]]]) {
acc[i[types[x]]] = [i]
}
else if (Array.isArray(acc[i[types[x]]])) {
acc[i[types[x]]].push(i);
}
else if (typeof acc[i[types[x]]] === 'object') {
acc[i[types[x]]] = [acc[i[types[x]]]]
acc[i[types[x]]].push(i)
}
return acc;
}, {})
v.push({ [types[x]]: data });
}
return v;
You were close, you just need to check if the property you were adding was undefined before adding. You can also check if the reduced object has any properties before adding to the result object.
Note that this may not be the most efficient way of doing it, but sometimes it's better to understand the code than it is to have highly efficient code.
const data = [{
foo: true
}, {
foo: false
}, {
bar: true
}, {
buzz: false
}];
let v = [];
let types = ['foo', 'bar', 'buzz', 'fizz']
for (let x = 0; x < types.length; x++) {
let reduced = data.reduce((acc, i) => {
// /* Added this type check */
if (!acc[i[types[x]]] && typeof i[types[x]] !== 'undefined') {
acc[i[types[x]]] = [i]
} else if (Array.isArray(acc[i[types[x]]])) {
acc[i[types[x]]].push(i);
} else if (typeof acc[i[types[x]]] === 'object') {
acc[i[types[x]]] = [acc[i[types[x]]]]
acc[i[types[x]]].push(i)
}
return acc;
}, {});
// Doesn't add a property for the type if there are no data
if (Object.keys(reduced).length) {
v.push({
[types[x]]: reduced
});
}
}
console.log(v);
Have a look at how Array.prototype.reduce works. It might be the right method to build your approach upon.
A generic way of solving the OP's problem was to iterate the provided data array. For each item one would extract its key and value. In case the item's key is listed (included) in another provided types array, one would continue creating a new data structure and collecting the currently processed item within the latter.
One does not want to iterate the types array for it will cause a unnecessarily complex lookup for the data items, each time a type item is going to be processed.
Thus a generically working (better code reuse) reduce method might be the best solution to the OP's problem ...
const sampleDataList = [
{ foo: true },
{ foo: false },
{ bar: true },
{ baz: false },
{ buzz: false },
{ baz: false },
{ bar: true }
];
// foo: {true: [{foo: true}], false: [{foo: false}]},
// bar: {true: [{bar: true}]},
// buzz: {false: [{buzz: false}]}
function collectItemIntoInclusiveKeyValueGroup(collector, item) {
const { inclusiveKeyList, index } = collector;
const firstItemEntry = Object.entries(item)[0];
const key = firstItemEntry[0];
const isProceedCollecting = ( // proceed with collecting ...
//
!Array.isArray(inclusiveKeyList) // - either for no given list
|| inclusiveKeyList.includes(key) // - or if item key is listed.
);
if (isProceedCollecting) {
let keyGroup = index[key]; // access the group identified
if (!keyGroup) { // by an item's key, ... or ...
// ...create it in case ...
keyGroup = index[key] = {}; // ...it did not yet exist.
}
const valueLabel = String(firstItemEntry[1]); // item value as key.
let valueGroupList = keyGroup[valueLabel]; // acces the group list
if (!valueGroupList) { // identified by an item's
// value, ...or create it in
valueGroupList = keyGroup[valueLabel] = []; // case it did not yet exist.
}
// push original reference into a grouped
// key value list, as required by the OP.
valueGroupList.push(item);
}
return collector;
}
console.log(
"'foo', 'bar', 'buzz' and 'fizz' only :",
sampleDataList.reduce(collectItemIntoInclusiveKeyValueGroup, {
inclusiveKeyList: ['foo', 'bar', 'buzz', 'fizz'],
index: {}
}).index
);
console.log(
"'foo', 'bar' and 'baz' only :",
sampleDataList.reduce(collectItemIntoInclusiveKeyValueGroup, {
inclusiveKeyList: ['foo', 'bar', 'baz'],
index: {}
}).index
);
console.log(
"all available keys :",
sampleDataList.reduce(collectItemIntoInclusiveKeyValueGroup, {
index: {}
}).index
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Try something like:
const data = [{foo: true},{foo: false},{bar: true},{buzz: false}];
let v = [];
let types = ['foo', 'bar', 'buzz', 'fizz' ];
for (let x = 0; x < types.length; x++) {
let filteredlist = data.filter(function (d) {
return Object.keys(d)[0] == types[x];
});
let isTrue = 0;
let isFalse = 0;
if (filteredlist.length > 0) {
for (let i = 0; i < filteredlist.length; i++) {
let trueOrfalse = eval("filteredlist[i]." + types[x]);
if (trueOrfalse) {
isTrue++;
} else {
isFalse++;
}
}
v.push(types[x], {true: isTrue, false: isFalse});
}
}
console.log(v);
Assuming you only want to count the number of each key (e.g. true or false) you can use the following code.
I've written this as a function named 'aggregate' so that it can be called multiple times with different arguments.
const initialData = [{foo: true},{foo: true},{foo: false},{bar: true},{buzz: false}];
const types = ['foo', 'bar', 'buzz', 'fizz'];
const aggregate = (data, types) => {
const result = {};
data.forEach(item => {
// Extract key & value from object
// Note: use index 0 because each object in your example only has a single key
const [key, value] = Object.entries(item)[0];
// Check if result already contains this key
if (result[key]) {
if (result[key][value]) {
// If value already exists, append one
result[key][value]++;
} else {
// Create new key and instantiate with value 1
result[key][value] = 1;
}
} else {
// If result doesn't contain key, instantiate with value 1
result[key] = { [value]: 1 };
}
});
return result;
};
console.log(aggregate(initialData, types));
This will output the following (note I've added another {foo: true} to your initialData array for testing).
The output should also be an object (not array) so that each key directly relates to its corresponding value, as opposed to an Array which will simply place the value as the next item in the Array (without explicitly linking the two).
{
foo: { true: 2, false: 1 },
bar: { true: 1 },
buzz: { false: 1 }
}
I have a nested state like this:
this.state = {
fields: {
subject: '',
from: {
name: '',
},
},
};
In an onChange function I'm handling updates to these nested values.
I'm trying to build a dynamically spread setState() for deep nests using dot notation.
For instance, with the array: const tree = ['fields','subject'] I'm able to update the subject state value with:
this.setState(prevState => ({
[tree[0]]: {
...prevState[tree[0]],
...(tree[2] ? {
...prevState[tree[1]],
[tree[2]]: value
}
: { [tree[1]]: value })
},
}));
Since the ternary operator is ending on { [tree[1]]: value }
But when my tree array is: const tree = ['fields','from','name'] the state value for fields.from.name is not changing, where it should be resolving to the first part of the ternary operator:
{
...prevState[tree[1]],
[tree[2]]: value
}
Am I missing something?
I've grown to prefer using libraries for these sorts of functions when it otherwise feels like I'm reinventing the wheel. lodash provides a set function (which also supports string paths):
_.set(object, path, value)
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
You'd also want to use _.cloneDeep(value) because _.set mutates the object.
this.state = {
fields: {
subject: '',
from: { name: '' },
},
};
const tree = ['fields', 'from', 'name']; // or 'fields.from.name'
this.setState(prevState => {
const prevState_ = _.cloneDeep(prevState);
return _.set(prevState_, tree, value);
});
You'll need a loop. For instance:
function update(prevState, tree, value) {
const newState = {};
let obj = newState;
for (let i = 0; i < tree.length; ++i) {
const name = tree[i];
if (i === tree.length - 1) {
obj[name] = value;
} else {
obj = obj[name] = {...prevState[name]};
}
}
return newState;
}
Live Example:
this.state = {
fields: {
subject: '',
from: {
name: '',
},
},
};
function update(prevState, tree, value) {
const newState = {};
let obj = newState;
let prev = prevState;
for (let i = 0; i < tree.length; ++i) {
const name = tree[i];
if (i === tree.length - 1) {
obj[name] = value;
} else {
const nextPrev = prev[name];
obj = obj[name] = {...nextPrev};
prev = nextPrev;
}
}
return newState;
}
const tree = ['fields','from','name']
const value = "updated";
console.log(update(this.state, tree, value));
I'm sure that can be shoehorned into a call to Array#reduce (because any array operation can be), but it wouldn't buy you anything.
The following code loops through a JavaScript object and collects only the properties that are arrays:
const building = this.building
let panoramaList = []
for (let key in building) {
const panoramas = building[key]
if (Array.isArray(panoramas)) {
panoramaList.push({ [key]: panoramas })
}
}
console.log(panoramaList)
In other words, it takes this:
{
name: '',
description: ''.
livingroom: Array[0],
study: Array[1],
bedroom: Array[0]
}
and turns it into this:
[
{ livingroom: Array[0] },
{ study: Array[1] },
{ bedroom: Array[0] }
]
However, what I need to produce is this:
{
livingroom: Array[0],
study: Array[1],
bedroom: Array[0]
}
How to accomplish that?
Change this :
const building = this.building
let panoramaList = []
for (let key in building) {
const panoramas = building[key]
if (Array.isArray(panoramas)) {
panoramaList.push({ [key]: panoramas })
}
}
console.log(panoramaList)
to this :
const building = this.building
let panoramaList = {}
for (let key in building) {
const panoramas = building[key]
if (Array.isArray(panoramas)) {
panoramaList[key]=panoramas
}
}
console.log(panoramaList)
Use Object.keys and try something like this:
var input = {} //...your input array
var keys = Object.keys(input);
var result = {};
keys.forEach(function (key) {
if (Array.isArray(input[key])) {
result[key] = input[key];
}
});
try this
var output = Object.keys(building).map(function(val){ return { val : building[val] } });
For the final output
var panoramaList = {}
Object.keys(building).forEach(function(val){
if ( Array.isArray(building[val] )
{
panoramaList[val] = building[val];
}
});
Make sure to define panoramaList as an object.
This works
var arrays = {
name: '',
description: '',
livingroom: ['1','www'],
study: ['2','sss'],
bedroom: ['3','aaa'],
Kitchen: ['4','bbb'],
}
const building = arrays
let panoramaList = {};
for (let key in building) {
const panoramas = building[key]
if (Array.isArray(panoramas)) {
panoramaList[key] = panoramas;
}
}
console.log(panoramaList);
https://jsbin.com/paqebupiva/1/edit?js,console,output
Rather than building a new object, you might just need to delete the unwanted properties from the object that you have:
var data = {
name: '',
description: '',
livingroom: [],
study: [1],
bedroom: [0]
};
Object.keys(data).forEach(function(key) {
if (!Array.isArray(data[key])) delete data[key];
})
document.write(JSON.stringify(data));