I tried to print "person[0].name: a" and "person[1].name: b" and so on, based on this person array of object:
I used object entries twice, any other way I can make the loop more efficient?
const person = [{
name: 'a',
}, {
name: 'b'
}]
Object.entries(person).forEach(([key, value]) => {
Object.entries(value).forEach(([key2, value2]) => {
console.log(`person[${key}].${key2}`, ':', value2)
})
})
You could take a recursive approach and hand over the parent path.
const
show = (object, parent) => {
const wrap = Array.isArray(object) ? v => `[${v}]` : v => `.${v}`;
Object.entries(object).forEach(([k, v]) => {
if (v && typeof v === 'object') show(v, parent + wrap(k));
else console.log(parent + wrap(k), v);
});
},
person = [{ name: 'a' }, { name: 'b' }];
show(person, 'person');
You don't really need the first call. person is already an Array.
const person = [{
name: 'a',
}, {
name: 'b'
}]
person.forEach((value, key) => {
Object.entries(value).forEach(([key2, value2]) => {
console.log(`person[${key}].${key2}`, ':', value2)
})
})
Just in case forEach is for side effects. If you want to create another array with transformed values you better use map/flatMap instead.
const person = [{
name: 'a',
}, {
name: 'b'
}]
const transformed = person.flatMap((value, key) => {
return Object.entries(value).map(([key2, value2]) => `person[${key}].${key2}:${value2}`)
})
console.log(transformed)
You can also try something like this, with only one forEach() loop
const person = [{
name: 'a',
},{
name: 'b'
}];
person.forEach((el,i) => {
let prop = Object.keys(el).toString();
console.log(`person[${i}].${prop}`, ':', el[prop])
});
Related
I need to perform filter in the array of objects to get all the keys. Although, whenever there is a obj inside of that key, I would need to get the key name and concat with the key name from the obj, so for example:
const data = [ id: 5, name: "Something", obj: { lower: True, higher: False } ]
result = ["id", "name", "obj.lower", "obj.higher"]
I could manage to do the above code, but, if there is more objs inside the data, I would need to keep adding a if condition inside of my logic, I would like to know if there is any other way, so it doesn't matter how many objects I have inside the objects, It will concat always.
The code I used from the above mention:
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const csvData = [
Object.keys(itemsArray[0]),
...itemsArray.map(item => Object.values(item))
].map(e => e.join(",")).join("\n")
// Separating keys
let keys = []
const allKeys = Object.entries(itemsArray[0]);
for (const data of allKeys) {
if (typeof data[1] === "object") {
const gettingObjKeys = Object.keys(data[1]);
const concatingKeys = gettingObjKeys.map((key) => data[0] + "." + key);
keys.push(concatingKeys);
} else {
keys.push(data[0])
}
}
//Flating
const flattingKeys = keys.reduce((acc, val: any) => acc.concat(val), []);
What I would like to achieve, lets suppose I have this array of object:
const data =
[
{ id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}}
...
]
Final result = ["id", "obj.name", "obj.obj2.name2", "obj.obj2.test"]
OBS: The first obj contains all the keys I need, no need to loop through other to get KEYS.
I would like to achieve, all the keys from the first object of the array, and if there is objects inside of objects, I would like to concat the obj names (obj.obj2key1)
You could map the key or the keys of the nested objects.
const
getKeys = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getKeys(v).map(s => `${k}.${s}`)
: k
),
getValues = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getValues(v)
: v
),
data = { id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
keys = getKeys(data),
values = getValues(data);
console.log(keys);
console.log(values);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const item = itemsArray[0];
const getAllKeys = (obj, prefix=[]) => {
if(typeof obj !== 'object'){
return prefix.join('.')
}
return Object.entries(obj).flatMap(([k, v]) => getAllKeys(v, [...prefix, k]))
}
console.log(getAllKeys(item))
The OP solution can be simplified by accepting a prefix param (the parent key) and a results param (defaulted to [] and passed into the recursion) to do the flattening...
let obj = { key0: 'v0', key1: { innerKey0: 'innerV0', innerInner: { deeplyNested: 'v' } }, key2: { anotherInnerKey: 'innerV' } }
function recursiveKeys(prefix, obj, result=[]) {
let keys = Object.keys(obj);
keys.forEach(key => {
if (typeof obj[key] === 'object')
recursiveKeys(key, obj[key], result);
else
result.push(`${prefix}.${key}`)
});
return result;
}
console.log(recursiveKeys('', obj))
function getKeys(obj) {
return Object.keys((typeof obj === 'object' && obj) || {}).reduce((acc, key) => {
if (obj[key] && typeof obj[key] === 'object') {
const keys = getKeys(obj[key]);
keys.forEach((k) => acc.add(`${key}.${k}`));
} else {
acc.add(key);
}
return acc;
}, new Set());
}
// accumulate the keys in a set (the items of the array may
// have different shapes). All of the possible keys will be
// stored in a set
const s = itemsArray.reduce(
(acc, item) => new Set([...acc, ...getKeys(item)]),
new Set()
);
console.log('Keys => ', Array.from(s));
You can use recursion as follows. Since typeof([1,3,5]) is object, we also have to confirm that value is not an array, !Array.isArray(value):
const obj = { id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}};
const getKeys = (o,p) => Object.entries(o).flatMap(([key,value]) =>
typeof(value) === 'object' && !Array.isArray(value) ?
getKeys(value, (p?`${p}.`:"") + key) :
(p ? `${p}.`: "") + key
);
console.log( getKeys(obj) );
This is the array i get:
const packages = [
{
id: '641a1690-6c8b-4ada-ae97-8d82cc4fe7a3',
name: 'com.sample',
children: {
id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f',
name: 'child.computer.com',
children: { id: 'e4ab-4a86-0f66cc32f560', name: 'child.com' }
}
},
{ id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f560', name: 'computer.com' },
{ id: 'ca7f972e-64ee-4cb0-80b9-1036fac69d32', name: 'java.util' }
];
So, it is an array of objects, and each object can have children, which again have id, name and possibly children (children is optional), and so on, it can be nested X times.
I want to change key names, id to key, name to title and children will remain children. So, my problem is that i don't know how to change keys inside children, i just change the first level and that is all.. It should be like:
{
key: '641a1690-6c8b-4ada-ae97-8d82cc4fe7a3',
title: 'com.sample',
children: {
key: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f',
title: 'child.computer.com',
children: { key: 'e4ab-4a86-0f66cc32f560', title: 'child.com' }
}
}
You can do this by using Recursion.
Check if the value of the [key-value] pair from the Object#entries() call is an object.
If so, call the transformObj function again recursively for that value. Else return the value as is.
And finally convert the array of [key-value] pairs back to an object by using Object#fromEntries:
const packages = [{ id: '641a1690-6c8b-4ada-ae97-8d82cc4fe7a3', name: 'com.sample', children: { id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f', name: 'child.computer.com', children: { id: 'e4ab-4a86-0f66cc32f560', name: 'child.com' }}}, { id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f560', name: 'computer.com' }, { id: 'ca7f972e-64ee-4cb0-80b9-1036fac69d32', name: 'java.util' }];
const replacer = { "id": "key", "name" :"title"};
const transformObj = (obj) => {
if(obj && Object.getPrototypeOf(obj) === Object.prototype){
return Object.fromEntries(
Object.entries(obj)
.map(([k, v]) => [replacer[k] || k, transformObj(v)])
);
}
//Base case, if not an Object literal return value as is
return obj;
}
console.log(packages.map(o => transformObj(o)));
You can try to go through every object inside your array and recursively iterate through its keys. Then you can change the keys you want to change and iterate further through the childrens key.
const packages = [{id: '641a1690-6c8b-4ada-ae97-8d82cc4fe7a3',name:'com.sample',children: {id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f',name: 'child.computer.com',children: { id: 'e4ab-4a86-0f66cc32f560', name: 'child.com' }}},{ id: 'd7384f60-e4ab-4a86-8e2e-0f66cc32f560', name: 'computer.com' },{ id: 'ca7f972e-64ee-4cb0-80b9-1036fac69d32', name: 'java.util' }];
const renameNestedObjects = (obj) => {
Object.keys(obj).forEach((key, index) => {
if (key == "id") {
obj["key"] = obj["id"];
delete obj["id"];
}
if (key == "name") {
obj["title"] = obj["name"];
delete obj["name"];
}
if (key == "children") {
renameNestedObjects(obj["children"]);
}
});
}
console.log(packages);
packages.forEach(obj => { renameNestedObjects(obj); });
console.log(packages);
Is the code below possible if it is manipulated correctly, it's currently not working for me, i also maybe be going about it the wrong way? Trying to filter and then map so that I can avoid duplicate 'Subnames'
App.js
render() {
const results = !this.state.resultFound
? null
: this.state.search.filter((item, index) =>
this.state.search.indexOf(item) === index).map(item => (
<Search
key={item.key}
subname={item.subname}
clickedSub={() => this.getUniqueSubs2(item)}
/>
))
)
return (
<div>
{results}
</div>
)
}
Search.js
const search = (props) => {
return (
<button onClick={props.clickedSub}> {props.subname} </button>
)
}
indexOf method will not work properly since you are not operating over primitive values but on objects. The strict comparison === will fail in this case.
You could try e.g. Array#reduce to get rid of the dupes.
const arr = [{ subname: 'foo', name: 'a' }, { subname: 'boo', name: 'b' }, { subname: 'foo', name: 'c' }];
const r = Object.values(
arr.reduce((s, a) => (a.subname in s ? a : s[a.subname] = a, s), {}),
);
console.log(r);
Echo of indexOf not working on objects, but an example with filter, indexOf, and map.
let search = [{
key: 1,
subname: 'a'
}, {
key: 2,
subname: 'a'
}, {
key: 3,
subname: 'b'
}, {
key: 4,
subname: 'b'
}]
let seen = [];
let result = search.filter((item, index) => {
if (seen.indexOf(item.subname) === -1) {
seen.push(item.subname)
return false
}
return true
})
.map(o => ({
newValue: 'abc',
...o
}))
document.querySelector('#test').innerHTML = JSON.stringify(result)
<h1 id='test'></h1>
This is my data
[{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',,grade:'A'},
{name:'ivan',,grade:'C'}]
I want to keep object that has grade A and C, it's easy I can just do filter like
person.filter(obj => obj.grade === 'A' || obj.grade === 'C')
but now I have an array of object.
[{grade:'A'},{grade:'C'}]
any clue how can I do filtering now? do I need nested loop?
Use Array.prototype.some:
let person = [{name:'james', grade:'A'},
{name:'john', grade:'B'},
{name:'iris', grade:'A'},
{name:'ivan', grade:'C'}];
let predicate = [{grade:'A'},{grade:'C'}];
let result = person.filter(obj => predicate.some(p => p.grade == obj.grade))
console.log('result:', result)
If your predicate is more dynamic than that, compare all object properties instead of just p.grade.
person.filter(obj => predicate.some(p => {
return Object.keys(p).every(k => obj[k] == p[k]);
}));
Using underscore lib
eg -
var bbb = [
{id: 839},
{id: 854}
];
var ids = {};
_.each(bbb, function (bb) { ids[bb.id] = true; });
var data = [{grade:'A'},{grade:'C'}];
var value = {};
_.each(data , function (d) { value[data.garde] === 'A' | value[data.garde] === 'C' ; });
Objects having grades A and C should be filtered as (classic js syntax),
var a = [
{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',grade:'A'},
{name:'ivan',grade:'C'}
];
a.filter(function(e) {
return (e.grade == 'A') || (e.grade == 'C');
});
If you have many grades to check (I don't know like all of them ;) ). You could first convert array into Set
const grades = new Set([{grade:'A'},{grade:'C'}].map(({grade}) => grade))
const persons = [{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',grade:'A'},
{name:'ivan',grade:'C'}]
And then filter persons array using has
const filtered = persons.filter(({grade}) => grades.has(grade))
You could use a Set for the predicates and filter accordingly
let person = [{ name: 'james', grade: 'A' }, { name: 'john', grade: 'B' }, { name: 'iris', grade: 'A' }, { name: 'ivan', grade: 'C' }],
predicate = [{ grade: 'A' }, { grade: 'C' }],
result = person.filter((s => p => s.has(p.grade))(new Set(predicate.map(p => p.grade))));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. 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 iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case