Javascript get parent of item in array - javascript

I am trying to get the parent of a specific (referenced) object in an array.
Example:
var data = [
{
key: "value1"
children: [
{
key: "value2"
},
{
key: "value3"
children: [
{
key: "value3a"
},
{
key: "value3b"
}
]
}
]
},
{
key: "value4"
}
];
When some stuff happens, I get the following:
var clicked = {
key: "value3a"
}
In this case I know that value3a has been clicked, and it's databound with the data variable.
The question is, how do I easily get the parent of clicked? It should return the whole children-array of value3 which I want:
[
{
key: "value3a"
},
{
key: "value3b"
}
]
Note: currently I am using UnderscoreJS to find the object of my array. So maybe UnderscoreJS could help?

Just create a child-parent map so that you can look up what you need:
var map = {};
function recurse(arr, parent) {
if (!arr) return;
for (var i=0; i<arr.length; i++) { // use underscore here if you find it simpler
map[arr[i].key] = parent;
recurse(arr[i].children, arr[i]);
}
}
recurse(data, {key:"root", children:data});
Now, in your event handler you can trivially use that map to look up your siblings:
map[clicked.key].children

You could use a recursive reduce function.
// Given
var data = [
{
key: "value1",
children: [
{
key: "value2"
},
{
key: "value3",
children: [
{
key: "value3a"
},
{
key: "value3b"
}
]
}
]
},
{
key: "value4"
}
];
var clicked = {
key: "value3a"
};
We can define a recursive reduce function, and give it the parent
as the context.
var rec_reduce = function(memo, obj) {
if(obj.key == clicked.key) {
return this || memo;
}
return _.reduce(obj.children, rec_reduce, memo, obj.children) || memo;
};
// Now we can lookup the key in clicked with one line
_.reduce(data, rec_reduce, null, data);
// Returns [{key: "value3a"}, {key: "value3b"}]
Or, if you want to leverage underscore to make a map as suggested in the first answer, that is even simpler:
var map = {};
var rec_map = function(obj, i, parent) {
map[obj.key] = parent;
_.each(obj.children, rec_map);
};
_.each(data, rec_map);
// Now getting the parent list is just a look up in the map
map[clicked.key]
// Returns [{key: "value3a"}, {key: "value3b"}]

Related

Loop through a nested Object until a certain value is found, Trees

me and my partner have been cracking our heads at this. we have to create a tree, that obviously has "children" below it.
all we need right now, is to loop over an object to find a certain value, if that value is not in that certain object, then go into its child property and look there.
basically what I'm asking is, how can you loop over nested objects until a certain value is found?
would super appreciate the perspective of a more experienced coder on this.
/// this is how one parent with a child tree looks like right now,
essentially if we presume that the child has another child in the children property,
how would we loop into that?
and maybe if that child also has a child, so on and so on...
Tree {
value: 'Parent',
children: [ Tree { value: 'Child', children: [] } ]
}
You can use recursive function to loop through objects:
const Tree = {
value: 'Parent',
children: [
{
value: 'Child1',
children: [
]
},
{
value: 'Child2',
children: [
{
value: 'Child2.1',
children: [
{
value: 'Child2.1.1',
children: [
]
},
{
value: 'Child2.1.2',
children: [
]
},
]
},
]
},
]
}
function findValue(obj, value)
{
if (obj.value == value)
return obj;
let ret = null;
for(let i = 0; i < obj.children.length; i++)
{
ret = findValue(obj.children[i], value);
if (ret)
break;
}
return ret;
}
console.log("Child1", findValue(Tree, "Child1"));
console.log("Child2.1", findValue(Tree, "Child2.1"));
console.log("Child3", findValue(Tree, "Child3"));
You can try this simple solution:
const myTree = {
value: 'Parent',
children: [
{
value: 'Child1',
children: []
},
{
value: 'Child2'
}
]
}
const isValueInTree = (tree, findValue) => {
if (tree.value === findValue) return true;
if (tree.children && tree.children.length !== 0) {
for (const branch of tree.children) {
const valuePresents = isValueInTree(branch, findValue);
if (valuePresents) return true;
}
}
return false;
}
console.log(isValueInTree(myTree, 'Child2'));
console.log(isValueInTree(myTree, 'Child'));
console.log(isValueInTree(myTree, 'Child1'));

How do you get results from within JavaScript's deep object?

expect result: ["human", "head", "eye"]
ex.
const data = {
name: "human",
children: [
{
name: "head",
children: [
{
name: "eye"
}
]
},
{
name: "body",
children: [
{
name: "arm"
}
]
}
]
}
const keyword = "eye"
Using the above data and using ffunction to obtain result
expect_result = f(data)
What kind of function should I write?
Thanks.
You could use an iterative and recursive approach by checking the property or the nested children for the wanted value. If found unshift the name to the result set.
function getNames(object, value) {
var names = [];
[object].some(function iter(o) {
if (o.name === value || (o.children || []).some(iter)) {
names.unshift(o.name);
return true;
}
});
return names;
}
var data = { name: "human", children: [{ name: "head", children: [{ name: "eye" }] }, { name: "body", children: [{ name: "arm" }] }] };
console.log(getNames(data, 'eye'));
console.log(getNames(data, 'arm'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try a recursive function
const getData = items => {
let fields = [];
for (const item of items) {
if (item.name) {
fields.push(item.name);
}
if (Array.isArray(item.children)) {
fields.push(...getData(item.children));
}
}
return fields;
}
var result = [] // This is outside the function since it get updated
function f(data) {
result.push(data.name); // Add the only name to the Array
if (data.children) { // just checking to see if object contains key children (staying safe)
for (var i = 0; i < data.children.length; i++) { // we are only looping through the children
f(data.children[i]); // Call this particular function to do the same thing again
}
}
}
Basically this function set the name. Then loop through the no of children and then calls a function to set the name of that child and then loop through its children too. Which happen to be a repercussive function till it finishes in order, all of them

Filter a dom object of input tags

I am building an object from a form that is currently rendered server side. I collect all the check boxes displayed in the image below and I am trying to sort them in a way that all the check boxes under each step (1, 2, 3 etc) is a single object based on the property parentNode.
Currently the document.querySelectorAll('.checkboxes') fetches all the checkboxes in following format.
var newObj = [
{
name: 'one',
parentNode: {
id: 'stepOne'
}
},
{
name: 'two',
parentNode: {
id: 'stepTwo'
}
},
{
name: 'three',
parentNode: {
id: 'stepOne'
}
},
]
The new object should be:
var newObj = {
stepOne: [
{
name: 'one',
parentNode: {
id: 'stepOne'
}
},
{
name: 'three',
parentNode: {
id: 'stepOne'
}
},
],
stepTwo: [
{
name: 'two',
parentNode: {
id: 'stepTwo'
}
},
]
}
Usually I do something like this:
let stepOne = function(step) {
return step.parentNode.getAttribute('id') === 'stepOne';
}
let stepTwo = function(step) {
return step.parentNode.getAttribute('id') === 'stepTwo';
}
let allTheStepOnes = fetchCheckBoxes.filter(stepOne);
But filter doesn't work on dom object and this seems inefficient as well.
Proper way of doing this is a forEach loop and using associative arrays like this:
let newObject = {};
originalObject.forEach((item)=>{
let step = item.parentNode.id
if (newObj[step] === undefined) {
newObj[step] = []
}
newObj[step].push(item)
})
Using reduce we can reduce your current array into the new structure.
return newObj.reduce(function(acc, item) {
If acc[item.parentNode.id] has been defined before, retrieve this. Otherwise set it to an empty array:
acc[item.parentNode.id] = (acc[item.parentNode.id] || [])
Add the item to the array and then return it:
acc[item.parentNode.id].push(item);
return acc;
We set the accumulator as {} to start with.
Snippet to show the workings.
var newObj = [{
name: 'one',
parentNode: {
id: 'stepOne'
}
}, {
name: 'two',
parentNode: {
id: 'stepTwo'
}
}, {
name: 'three',
parentNode: {
id: 'stepOne'
}
}, ];
var newOrder = function(prevList) {
return prevList.reduce(function(acc, item) {
acc[item.parentNode.id] = (acc[item.parentNode.id] || [])
acc[item.parentNode.id].push(item);
return acc;
}, {});
}
console.log(newOrder(newObj));
This function should do the trick
function mapObj(obj) {
var result = {};
for(var i = 0; i < obj.length; i++) {
var e = obj[i];
result[e.parentNode.id] = result[e.parentNode.id] || [];
result[e.parentNode.id].push(e);
}
return result;
}

How to add non duplicate objects in an array in javascript?

I want to add non-duplicate objects into a new array.
var array = [
{
id: 1,
label: 'one'
},
{
id: 1,
label: 'one'
},
{
id: 2,
label: 'two'
}
];
var uniqueProducts = array.filter(function(elem, i, array) {
return array.indexOf(elem) === i;
});
console.log('uniqueProducts', uniqueProducts);
// output: [object, object, object]
live code
I like the class based approach using es6. The example uses lodash's _.isEqual method to determine equality of objects.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
class UniqueArray extends Array {
constructor(array) {
super();
array.forEach(a => {
if (! this.find(v => _.isEqual(v, a))) this.push(a);
});
}
}
var unique = new UniqueArray(array);
console.log(unique);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
Usually, you use an object to keep track of your unique keys. Then, you convert the object to an array of all property values.
It's best to include a unique id-like property that you can use as an identifier. If you don't have one, you need to generate it yourself using JSON.stringify or a custom method. Stringifying your object will have a downside: the order of the keys does not have to be consistent.
You could create an objectsAreEqual method with support for deep comparison, but this will slow your function down immensely.
In two steps:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
// Create a string representation of your object
function getHash(obj) {
return Object.keys(obj)
.sort() // Keys don't have to be sorted, do it manually here
.map(function(k) {
return k + "_" + obj[k]; // Prefix key name so {a: 1} != {b: 1}
})
.join("_"); // separate key-value-pairs by a _
}
function getHashBetterSolution(obj) {
return obj.id; // Include unique ID in object and use that
};
// When using `getHashBetterSolution`:
// { '1': { id: '1', label: 'one' }, '2': /*etc.*/ }
var uniquesObj = array.reduce(function(res, cur) {
res[getHash(cur)] = cur;
return res;
}, {});
// Convert back to array by looping over all keys
var uniquesArr = Object.keys(uniquesObj).map(function(k) {
return uniquesObj[k];
});
console.log(uniquesArr);
// To show the hashes
console.log(uniquesObj);
You can use Object.keys() and map() to create key for each object and filter to remove duplicates.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
var result = array.filter(function(e) {
var key = Object.keys(e).map(k => e[k]).join('|');
if (!this[key]) {
this[key] = true;
return true;
}
}, {});
console.log(result)
You could use a hash table and store the found id.
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
uniqueProducts = array.filter(function(elem) {
return !this[elem.id] && (this[elem.id] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
Check with all properties
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
keys = Object.keys(array[0]), // get the keys first in a fixed order
uniqueProducts = array.filter(function(a) {
var key = keys.map(function (k) { return a[k]; }).join('|');
return !this[key] && (this[key] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
You can use reduce to extract out the unique array and the unique ids like this:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
if(prev.ids.indexOf(curr.id) === -1) {
prev.array.push(curr);
prev.ids.push(curr.id);
}
return prev;
}, {array: [], ids: []});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
If you don't know the keys, you can do this - create a unique key that would help you identify duplicates - so I did this:
concat the list of keys and values of the objects
Now sort them for the unique key like 1|id|label|one
This handles situations when the object properties are not in order:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
var tracker = Object.keys(curr).concat(Object.keys(curr).map(key => curr[key])).sort().join('|');
if(!prev.tracker[tracker]) {
prev.array.push(curr);
prev.tracker[tracker] = true;
}
return prev;
}, {array: [], tracker: {}});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}

Javascript Nested Literal to string

I am looking for a technique to run over a object of nested properties and wish to join the properties'.
This is the object I'd like to join:
var array = {
prop1: {
foo: function() {
// Your code here
}
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
}
};
The result should look like this:
[
[ 'prop1', 'foo' ],
[ 'prop2', 'bar1' ],
[ 'prop2', 'bar2' ]
]
Then I'd like to join the array to strings formatted like this:
prop1.foo
prop2.bar1
prop2.bar2
Any tips?
EDIT: Forgot to say it should work for deeper arrays too.
Something along these lines? http://jsfiddle.net/X2X2b/
var array = {
prop1: {
foo: function() {
// Your code here
}
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
}
};
var newA = [],
newB = [];
for ( var obj in array ) {
for (var inObj in array[obj]) {
newA.push([obj, inObj]);
newB.push(obj + '.' + inObj);
}
}
console.log(newA);
console.log(newB);
This is quite a different problem now that you have specified that it needs to support arbitrary depths. In order to solve it we need to use recursion and we need to use a second recursive parameter which keeps track of where we are in the nested hierarchy.
function objectPropertiesToArrays(obj, prepend) {
// result will store the final list of arrays
var result = [];
// test to see if this is a valid object (code defensively)
if(obj != null && obj.constructor === Object) {
for (var propertyName in obj) {
var property = obj[propertyName],
// clone prepend instantiate a new array
list = (prepend || []).slice(0);
// add the property name to the list
list.push(propertyName);
// if it isn't a nested object, we're done
if (property.constructor !== Object) {
result.push(list);
// if it is a nested object, recurse
} else {
// recurse and append the resulting arrays to our list
result = result.concat(objectPropertiesToArrays(property, list));
}
}
}
return result;
}
Example:
var obj = {
prop1: {
foo: function() { }
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
},
prop3: {
x: {
y: [],
z: 'test'
},
erg: 'yar'
}
};
objectPropertiesToArrays(obj);
Returns
[
["prop1", "foo"],
["prop2", "bar1"],
["prop2", "bar2"],
["prop3", "x", "y"],
["prop3", "x", "z"],
["prop3", "erg"]
]

Categories

Resources