How to use reducer in neasted array - javascript

trying to delete id:2 car from my mongodb database. using react redux, my question is how to check dispatch id equal to car id in reducer?
button
<button onClick={this.handleDeleteCar(carID : data.id)>delete</button>
data log
carview:
[
{ id: 5c7496f0def4602bf413ea9a,
parentId: 5c7496f0def4602bf413ea97,
car: [
{
id: 2,
name: bmw
},
{
id:3,
name: amg
}
]
}
]
reducer
case carConstants.car_DELETE_REQUEST:
return {...state,items: state.items.map(car =>car.id === action.id
? { ...car, deleting: true }
: car
)
};

Use Array.prototype.filter()
return {
...state,
items: state.items.filter((car) => car.id !== action.id)
}
EDIT:
In your case, you can use an Array.prototype.filter nested inside an Array.prototype.map:
return {
...state,
carView: state.carView.map((view) => {
return {
...view,
car: view.car.filter(({ id }) => id !== action.id)
}
})
}
This can become quickly messy if you deal with deeply nested objects and it's a good idead to try to keep your state normalized in redux

Above answer using Array.filter is a good one, but if that won't fit your use case you can use a nested findIndex check.
let state = [
{ id: "5c7496f0def4602bf413ea9a",
parentId: "5c7496f0def4602bf413ea97",
car: [
{
id: 4,
name: "bmw"
},
{
id:5,
name: "amg"
}
]
},
{ id: "5c7496f0def4602bf413ea9a",
parentId: "5c7496f0def4602bf413ea97",
car: [
{
id: 2,
name: "bmw"
},
{
id:3,
name: "amg"
}
]
}
];
const action = { type: 'example', id: 2 }
const index = state.findIndex(({car}) => car.findIndex(({id}) => id === action.id) !== -1);
if (index === -1) {
// you would probably return current state here realistically
throw new Error('no index');
}
console.log(index);
state = [ ...state.slice(0, index), ...state.slice(index + 1)];
console.log(state);

Related

How to find a key by value in Javascript?

I'm new to Javascript and react. I have a react app with the following array and a function to find a key inside the array. I'm providing the search value as parameter and I need to find the key which has the search value. I have three user roles as client, admin and manager. I'm doing the code as follows
Array:
{
client:[{ id:1, name:"Adam" },
{ id:2, name:"Mon" },
{ id:3, name:"Sara" }],
admin:[{ id:4, name:"Jake" },
{ id:5, name:"Jon" },
{ id:6, name:"Sean" }],
manager:[{ id:7, name:"Doe" },
{ id:8, name:"Matt" },
{ id:9, name:"Mark" }]
}
I need to find the user role by given id. This is what I tried.
Component:
roleCheck = (searchId) => {
var roles = this.state.array;
Object.keys(roles).forEach(role => {
Object.keys(role).forEach(user => {
if (user.id === searchId){
return role;
}
});
});
}
The result is always Undefined. How can I solve this?
You're not returning any thing from you function. You can change your coe in following manner
roleCheck = (searchId) => {
var roles = this.state.array;
let output = 'Not found'
Object.keys(roles).forEach(role => {
Object.keys(role).forEach(user => {
if (user.id === searchId){
output = role
}
});
});
return output;
}
Or alternatively You can use find and some
let data = {client:[{ id:1, name:"Adam" },{ id:2, name:"Mon" },{ id:3, name:"Sara" }],admin:[{ id:4, name:"Jake" },{ id:5, name:"Jon" },{ id:6, name:"Sean" }],manager:[{ id:7, name:"Doe" },{ id:8, name:"Matt" },{ id:9, name:"Mark" }]}
let role = (searchId) => {
return Object.keys(data).find(key => {
return data[key].some(({id})=> id === searchId)
})
}
console.log(role(6))
console.log(role(60))
Your return will only return from the function given to forEach and not the checkRole function.
You could instead use a combination of find and some to figure out what role the person with the searchId has:
const data = {
client: [
{ id: 1, name: "Adam" },
{ id: 2, name: "Mon" },
{ id: 3, name: "Sara" }
],
admin: [
{ id: 4, name: "Jake" },
{ id: 5, name: "Jon" },
{ id: 6, name: "Sean" }
],
manager: [
{ id: 7, name: "Doe" },
{ id: 8, name: "Matt" },
{ id: 9, name: "Mark" }
]
};
function roleCheck(searchId) {
return Object.keys(data).find(key => {
return data[key].some(person => person.id === searchId);
});
}
console.log(roleCheck(8));
using the https://underscorejs.org/ library us can do this
var hash = {
foo: 1,
bar: 2
};
(_.invert(hash))[1]; // => 'foo'
you can put this into your loop
Another way to check if a JavaScript Object has a key is using hasOwnProperty method.
let c = {"name": "John", "id": 12};
c.hasOwnProperty("name") // true

Get specific key depth in object with key value

const item = {
id: 'item1',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3' },
]
},
{ id: 'item1-2',
children: [
{ id: 'item1-2-1' }
]
}
]
}
Like this,
function getLevelOfId(){
...
}
getLevelOfId('item1') =====> return 1
getLevelOfId('item1-2') =====> return 2
getLevelOfId('item1-1-1') =====> return 3
getLevelOfId('item1-1-2') =====> return 3
How to get specific object's depth with JavaScript?
Not use of id string. like ('item1-2').split('-').length Because each object has randomic id. Is there a simple way?
You need to iterate all objects and if found, take one for each level for the recursion depth.
function getLevelOfId(object, id) {
var level;
if (object.id === id) return 1;
object.children && object.children.some(o => level = getLevelOfId(o, id));
return level && level + 1;
}
const item = { id: 'item1', children: [{ id: 'item1-1', children: [{ id: 'item1-1-1' }, { id: 'item1-1-2' }, { id: 'item1-1-3' }] }, { id: 'item1-2', children: [{ id: 'item1-2-1' }] }] };
console.log(getLevelOfId(item, 'item1')); // 1
console.log(getLevelOfId(item, 'item1-2')); // 2
console.log(getLevelOfId(item, 'item1-1-1')); // 3
console.log(getLevelOfId(item, 'item1-1-2')); // 3
console.log(getLevelOfId(item, 'foo')); // undefined
if the structure id & children is fixed, how about search the whole value like "item1-1-1" in the json string:
{"id":"item1","children":[{"id":"item1-1","children":[{"id":"item1-1-1"},{"id":"item1-1-2"},{"id":"item1-1-3"}]},{"id":"item1-2","children":[{"id":"item1-2-1"}]}]}
level = (number of "{") - (number of "}") // before the searched positon of the string

How to sort the array of objects in javascript?

Hi i want to sort an array of objects in javascript. Below is the example data.
const example = [
{
name: "c_name",
children: [{
name: "child",
email: "child1#dev.com",
children: [{
name: "nested_child",
email: "nestedchild1#dev.com",
}]
}]
},
{
name: "a_name",
children: [{
name: "some_name",
email: "some_name#dev.com",
children: []
}]
},
{
name: "name",
children: [{
name: "child_name",
email: "child_name#dev.com",
children: []
}]
}
];
Should sort this array based on property 'name' and the children object should be sorted again based on 'name' property.
So the expected output is like below, and would like to retain other properties as well like email property in children.
a_name
some_name
c_name
child
nested_child
name
child_name
What i have done...i have a sort function that sorts the array by name property. however dont know how to sort the children object with name property.
const sorted_example = example.sort(this.sort_by_name());
sort_by_name = () => {
return (a, b) => {
let result;
const a_value = a.name.toLowerCase();
const b_value = b.name.toLowerCase();
if (a_value > b_value) {
result = 1;
} else if (a_value < b_value) {
result = -1;
} else {
result = 0;
}
return result;
};
};
Could someone help me how to continue with this. thanks.
The previous answers got you there most of the way but you need to sort again if an item has children. In my example I don't mutate the original array (use .slice to make a shallow copy so .sort doesn't mutate).
const example = [{"name":"c_name","children":[{"name":"child","email":"child1#dev.com","children":[{"name":"nested_child","email":"nestedchild1#dev.com"}]},{"name":"b"},{"name":"a"}]},{"name":"a_name","children":[{"name":"some_name","email":"some_name#dev.com","children":[]}]},{"name":"name","children":[{"name":"child_name","email":"child_name#dev.com","children":[]}]}];
const sortRecursive = (data) => {
const recur = (arr) =>
arr
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
//check each item to see if it has children that is an array
.map(
(item) =>
//if item has children that is an array then sort that
// and it's childrens childrens children
Array.isArray(item.children)
? {
...item,
children: recur(item.children),
}
: item, //no children, just return the item
);
return recur(data);
};
//note that sortRecursive does not change example but returns a new array
// that is sorted
console.log(sortRecursive(example));
assuming your children are arrays instead of objects as per your example:
const example = [
{
name: "c_name",
children: [{
name: "child",
email: "child1#dev.com",
children: [{
name: "nested_child",
email: "nestedchild1#dev.com",
}]
}]
},
{
name: "a_name",
children: [{
name: "some_name",
email: "some_name#dev.com",
children: []
}]
},
{
name: "name",
children: [{
name: "child_name",
email: "child_name#dev.com",
children: []
}]
}
];
a quick way would be:
example
.sort((a, b) => a.name.localeCompare(b.name))
.map(m => {
return {
name: m.name,
children: m.children.sort((a, b) => a.name.localeCompare(b.name))
};
});
You can simply use the sort() method
example.sort((el, q) => el.name.localeCompare(q.name))

How to find a object in a nested array using recursion in JS

Consider the following deeply nested array:
const array = [
{
id: 1,
name: "bla",
children: [
{
id: 23,
name: "bla",
children: [{ id: 88, name: "bla" }, { id: 99, name: "bla" }]
},
{ id: 43, name: "bla" },
{
id: 45,
name: "bla",
children: [{ id: 43, name: "bla" }, { id: 46, name: "bla" }]
}
]
},
{
id: 12,
name: "bla",
children: [
{
id: 232,
name: "bla",
children: [{ id: 848, name: "bla" }, { id: 959, name: "bla" }]
},
{ id: 433, name: "bla" },
{
id: 445,
name: "bla",
children: [
{ id: 443, name: "bla" },
{
id: 456,
name: "bla",
children: [
{
id: 97,
name: "bla"
},
{
id: 56,
name: "bla"
}
]
}
]
}
]
},
{
id: 15,
name: "bla",
children: [
{
id: 263,
name: "bla",
children: [{ id: 868, name: "bla" }, { id: 979, name: "bla" }]
},
{ id: 483, name: "bla" },
{
id: 445,
name: "bla",
children: [{ id: 423, name: "bla" }, { id: 436, name: "bla" }]
}
]
}
];
How would I grab a certain object by key that might be deeply nested, using recursion?
I have tried this, but this won't work for nesting deeper than 2 levels, it then just returns undefined:
const findItemNested = (arr, itemId, nestingKey) => {
for (const i of arr) {
console.log(i.id);
if (i.id === itemId) {
return i;
}
if (i[nestingKey]) {
findItemNested(i[nestingKey], itemId, nestingKey);
}
}
};
The result should be:
const res = findItemNested(array, 959, "children"); >> { id: 959, name: "bla" }
This can perhaps also be achieved using .find, or just to flatten the array (by the children key), but using recursion seems like the most logical solution to me. Does anybody have a solution to this?
Thanks in advance :).
You might use a recursive reduce:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => (
arr.reduce((a, item) => {
if (a) return a;
if (item.id === itemId) return item;
if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
}, null)
);
const res = findItemNested(array, 959, "children");
console.log(res);
This should work:
function findByIdRecursive(array, id) {
for (let index = 0; index < array.length; index++) {
const element = array[index];
if (element.id === id) {
return element;
} else {
if (element.children) {
const found = findByIdRecursive(element.children, id);
if (found) {
return found;
}
}
}
}
}
You might also use recursion with Array.find like below
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
function findById(arr, id, nestingKey) {
// if empty array then return
if(arr.length == 0) return
// return element if found else collect all children(or other nestedKey) array and run this function
return arr.find(d => d.id == id)
|| findById(arr.flatMap(d => d[nestingKey] || []), id)
|| 'Not found'
}
console.log(findById(array, 12, 'children'))
console.log(findById(array, 483, 'children'))
console.log(findById(array, 1200, 'children'))
We use object-scan for most of our data processing. It's awesome for all sorts of things, but does take a while to wrap your head around. This is how one could answer your question:
// const objectScan = require('object-scan');
const find = (data, id) => objectScan(['**(^children$).id'], {
abort: true,
rtn: 'parent',
useArraySelector: false,
filterFn: ({ value }) => value === id
})(data);
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
console.log(find(array, 12));
// => { id: 12, name: 'bla', children: [ { id: 232, name: 'bla', children: [ { id: 848, name: 'bla' }, { id: 959, name: 'bla' } ] }, { id: 433, name: 'bla' }, { id: 445, name: 'bla', children: [ { id: 443, name: 'bla' }, { id: 456, name: 'bla', children: [ { id: 97, name: 'bla' }, { id: 56, name: 'bla' } ] } ] } ] }
console.log(find(array, 483));
// => { id: 483, name: 'bla' }
console.log(find(array, 959));
// => { id: 959, name: 'bla' }
console.log(find(array, 1200));
// => undefined
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
You can do:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => arr.reduce((a, c) => {
return a.length
? a
: c.id === itemId
? a.concat(c)
: c[nestingKey]
? a.concat(findItemNested(c[nestingKey], itemId, nestingKey))
: a
}, []);
const res = findItemNested(array, 959, "children");
if (res.length) {
console.log(res[0]);
}
This will use recursive find by level, it'll try to find the item in array and then call itself with the children of each item in the array:
New browsers will have Array.prototype.flatten but in this case I've added the flatten function separately.
const array = [{"id":1,"name":"bla","children":[{"id":23,"name":"bla","children":[{"id":88,"name":"bla"},{"id":99,"name":"bla"}]},{"id":43,"name":"bla"},{"id":45,"name":"bla","children":[{"id":43,"name":"bla"},{"id":46,"name":"bla"}]}]},{"id":12,"name":"bla","children":[{"id":232,"name":"bla","children":[{"id":848,"name":"bla"},{"id":959,"name":"bla"}]},{"id":433,"name":"bla"},{"id":445,"name":"bla","children":[{"id":443,"name":"bla"},{"id":456,"name":"bla","children":[{"id":97,"name":"bla"},{"id":56,"name":"bla"}]}]}]},{"id":15,"name":"bla","children":[{"id":263,"name":"bla","children":[{"id":868,"name":"bla"},{"id":979,"name":"bla"}]},{"id":483,"name":"bla"},{"id":445,"name":"bla","children":[{"id":423,"name":"bla"},{"id":436,"name":"bla"}]}]}];
const flatten = (arr) =>
arr.reduce((result, item) => result.concat(item), []);
const findBy = (findFunction, subItemsKey) => (array) =>
//array is empty (can be when children of children of children does not exist)
array.length === 0
? undefined //return undefined when array is empty
: array.find(findFunction) || //return item if found
findBy(findFunction, subItemsKey)(//call itself when item is not found
flatten(
//take children from each item and flatten it
//([[child],[child,child]])=>[child,child,child]
array.map((item) => item[subItemsKey] || []),
),
);
const findChildrenById = (array) => (value) =>
findBy((item) => item.id === value, 'children')(array);
const findInArray = findChildrenById(array);
console.log('found', findInArray(99));
console.log('not found', findInArray({}));
You need to iterate through your objects and then need to be parse each object using recursion. Try the answer mentioned here: JavaScript recursive search in JSON object
code:
`function findNode(id, currentNode) {
var i,
currentChild,
result;
if (id == currentNode.id) {
return currentNode;
} else {
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
for (i = 0; i < currentNode.children.length; i += 1) {
currentChild = currentNode.children[i];
// Search in the current child
result = findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}`

JavaScript Object Array: Removing objects with duplicate properties

I have an array of objects:
[
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
I'd like to strip out objects with duplicate Ids, leaving an array that would look like this:
[
{ id: 1, name: "Bob" },
{ id: 2, name: "Daryl" }
]
I don't care which objects are left, as long as each ID is unique. Anything in Underscore, maybe, that would do this?
Edit: This is not the same as the duplicate listed below; I'm not trying to filter duplicate OBJECTS, but objects that contain identical IDs. I've done this using Underscore - I'll post the answer shortly.
You can use reduce and some to good effect here:
var out = arr.reduce(function (p, c) {
// if the next object's id is not found in the output array
// push the object into the output array
if (!p.some(function (el) { return el.id === c.id; })) p.push(c);
return p;
}, []);
DEMO
the es6 way
function removeDuplicates(myArr, prop) {
return myArr.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
})
}
Test it
let a =[
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
console.log( removeDuplicates( a, 'id' ) )
//output [
{ id: 1, name: "Bob" },
{ id: 2, name: "Daryl" }
]
If you use underscore, you can use the _uniq method
var data = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
_.uniq(data, function(d){ return d.ID });
Produces a duplicate-free version of the array, using === to test object equality. In particular only the first occurence of each value is kept. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.
Source: http://underscorejs.org/#uniq
Can use es6 Map collection mix with reduce
const items = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
const uniqItems = [...items.reduce((itemsMap, item) =>
itemsMap.has(item.id) ? itemsMap : itemsMap.set(item.id, item)
, new Map()).values()]
console.log(uniqItems);
Using findIndex should be the simplest solution.
array.filter((elem, index, arr) => arr.findIndex(e => e.id === elem.id) === index)
You can simply filter the array, but you'll need an index of existing IDs that you've already used...
var ids = [];
var ar = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
];
ar = ar.filter(function(o) {
if (ids.indexOf(o.id) !== -1) return false;
ids.push(o.id);
return true;
});
console.log(ar);
Here's some documentation on filter()...
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Categories

Resources