I have the following array and object I would like to 'match'
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
}
I would like to have the following end result:
const ownersWithName = {
'Alex': [{ key: 1, name: 'A', }, { key: 5, name: 'E' }],
'Barb': [{ key: 2, name: 'B', }, { key: 4, name: 'D' }],
'John': [{ key: 3, name: 'C', }, ],
}
So far my solution is this:
function matchOwners (items, owners) {
const ownersWithName = {};
for (const item of items) {
if (owners[item.owner]) {
if (ownersWithName[item.owner]) {
ownersWithName[item.owner] = [ ...ownersWithName[item.owner], item];
} else {
ownersWithName[item.owner] = [item];
}
}
}
return ownersWithName;
}
This solution works, but i feel it's too verbose. i tried to use the spread operator without the if condition, but this needs the array to exist already, otherwise i get the error ownersWithName[item.owner] is not iterable. Is there a better way to do this?
Something like (completely untested):
ownersWithName = items.reduce((result, item) => {
if (owners[item.owner]) {
if (!(item.owner in result)) {
result[item.owner] = [];
}
result[item.owner].push({key: item.key, name: item.name});
}
return result;
}, {})
You can also simply achieve this by using Array.forEach() along with the Object.keys() method.
Live Demo (Descriptive comments has been added in the below code snippet) :
// Input array
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
// Input object which should be used to match.
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
};
// Declare an object which will store the final result.
const resultObj = {};
// Iterating over an items array to manipulate the data and make the final result set.
items.forEach(obj => {
resultObj[obj.owner] = !resultObj[obj.owner] ? [] : resultObj[obj.owner];
Object.keys(owners[obj.owner]).forEach(key => {
if (key == obj.key) {
resultObj[obj.owner].push({
key: obj.key,
name: obj.name
});
}
});
});
// Final output
console.log(resultObj);
Related
I am trying to sort object of arrays by arrays length, for example, i try to get from this object array where length more than 3
{
friend: [
{ id: 0, name: 'A' },
{ id: 1, name: 'V' },
{ id: 2, name: 'C' },
{ id: 3, name: 'D' }
],
people: [ { id: 0, name: 'A' }, { id: 1, name: 'B' } ]
}
and the result will array friend. I try to do this
function getPart (obj) {
for (let key in obj) {
if (obj[key].length>=4) {
console.log(obj[key]);
}
}}
but I want to try to do it with filter object but I have no idea how to make it
We can achieve that using Object.entries() and reduce() function.
Here's an example:
JS
let data = {
friend: [
{ id: 0, name: 'A' },
{ id: 1, name: 'V' },
{ id: 2, name: 'C' },
{ id: 3, name: 'D' }
],
people: [ { id: 0, name: 'A' }, { id: 1, name: 'B' } ]
}
// Convert first the object to an array using Object.entries so we can loop it using reduce() function
// Then we make a condition statement if the object's length is >= 4, then we'll filter that to the new object.
let new_data = Object.entries(data).reduce((a, b) => a = {...a, ...(b[1].length >= 4 ? {[b[0]]: b[1]} : {})} ,{})
console.log(new_data) // Expected result: { friend: [Object] }
Just one line:
Object.values(obj).filter(ob => ob.length >= 4);
const ob = {
friend: [
{ id: 0, name: 'A' },
{ id: 1, name: 'V' },
{ id: 2, name: 'C' },
{ id: 3, name: 'D' }
],
people: [ { id: 0, name: 'A' }, { id: 1, name: 'B' } ]
}
function getPart (obj) {
const filtered = Object.values(obj).filter(ob => ob.length >= 4);
console.log(filtered);
}
getPart(ob);
I'm wondering, I have the following data structure:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
...
{
name: 'Zulu',
},
{
name: 'Zulo',
},
]
I'm expecting there to be at least one, usually more, key for each letter of the alphabet. However, if there isn't a single data.name I would still like in the below data structure to have an empty domains array [].
I was wondering, how could this be manipulated into the following data structure:
data = {
a: {
domains: [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
],
},
b: {
domains: [
...
]
},
...
z: {
domains: [
...
]
},
};
I have used a few methods, which involved a pre-constructed "alphbetised" key = object array, then filtered each on the first letter of the data.name value...but I was wondering if there was a standard and performant method to acheive this?
Using reduce()
const data = [{name:"Alpha"},{name:"Alfa"},{name:"Bravo"},{name:"Brafo"},{name:"Charlie"},{name:"Charly"},{name:"Zulu"},{name:"Zulo"}]
const res = data.reduce((a, v) => {
// prepare key
let key = v.name.substring(0,1).toLowerCase()
// check key in accumulator
if (!a[key]) {
// assign domain object
a[key] = {domains: []}
}
// push domain array
a[key].domains.push(v)
return a
}, {})
console.log(res)
Here is what you want:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
{
name: 'Zulu',
},
{
name: 'Zulo',
},
];
console.log(data.reduce((a, c) => {
const firstLetter = c.name[0].toLowerCase();
if (a[firstLetter]) {
a[firstLetter].domains.push(c);
} else {
a[firstLetter] = { domains: [c] };
}
return a;
}, {}));
I have the following array of deeply nested objects:
const data = [
{
name: "foo",
children:[
{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [
{
count: 3,
name: "C",
children: [
{
count: 4,
name: "D"
}
]
}
]
}
]
The way I'd like to transform this would be such as:
const expectedStructure = [
{
count: 1,
name: "A",
label: "foo = A"
},
{
count: 2,
name: "B",
label: "foo = B"
},
{
count: 3,
name: "C",
label: "bar = C"
},
{
count: 4,
name: "D",
label: "bar = D"
}
]
I created recursive function that transforms nested array into array of flat objects.
Here's my code:
function getChildren(array, result=[]) {
array.forEach(({children, ...rest}) => {
result.push(rest);
if(children) {
getChildren(children, result);
}
});
return result;
}
And here's output I get:
[ { name: 'foo' },
{ count: 1, name: 'A' },
{ count: 2, name: 'B' },
{ name: 'bar' },
{ count: 3, name: 'C' },
{ count: 4, name: 'D' } ]
The problem is that I need to add label field to every object in my output array, and I can't find a solution without iterating multiple times through the final array to make desired transformation. How to properly insert label field without hugely augmenting complexity of the function?
Check each iteration whether the current item is a "parent" item, and reassign label if it is.
const data = [{name:"foo",children:[{count:1,name:"A"},{count:2,name:"B"}]},{name:"bar",children:[{count:3,name:"C",children:[{count:4,name:"D"}]}]}];
function getChildren(array, result = [], label = "") {
array.forEach(({ children, name, count }) => {
if (!label || name[1]) {
label = `${name} = `;
}
if (count) {
result.push({ count, name, label: label + name });
}
if (children) {
getChildren(children, result, label);
}
});
return result;
}
const res = getChildren(data);
console.log(res);
You can use a different function for the nested levels, so you can pass the top-level name properties down through all those recursion levels.
function getTopChildren(array, result = []) {
array.forEach(({
name,
children
}) => {
if (children) {
getChildren(children, name, result);
}
});
return result;
}
function getChildren(array, name, result) {
array.forEach(({
children,
...rest
}) => {
rest.label = `${name} = ${rest.name}`;
result.push(rest);
if (children) {
getChildren(children, name, result);
}
});
}
const data = [{
name: "foo",
children: [{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [{
count: 3,
name: "C",
children: [{
count: 4,
name: "D"
}]
}]
}
]
console.log(getTopChildren(data));
You can also do this recursively with flatMap based on whether or not a parent has been passed into the recursive call :
const data = [{
name: "foo",
children: [{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [{
count: 3,
name: "C",
children: [{
count: 4,
name: "D"
}]
}]
}
];
function flatten(arr, parent = null) {
return parent
? arr.flatMap(({name, count, children}) => [
{name, count, label: `${parent} = ${name}`},
...flatten(children || [], parent)
])
: arr.flatMap(({name, children}) => flatten(children || [], name));
}
console.log(flatten(data));
Sometimes it's a little easier to reason about the code and write it clearly using generators. You can yield* from the recursive calls:
const data = [{name: "foo",children:[{count: 1,name: "A"},{ count: 2,name: "B"}]},{name: "bar",children: [{count: 3,name: "C",children: [{count: 4,name: "D"}]}]}]
function* flat(input, n){
if (!input) return
if (Array.isArray(input)) {
for (let item of input)
yield* flat(item, n)
}
let _name = n || input.name
if ('count' in input) {
yield { count:input.count, name:input.name, label:`${_name} = ${input.name}`}
}
yield* flat(input.children, _name)
}
let g = [...flat(data)]
console.log(g)
The function returns a generator, so you need to spread it into a list [...flat(data)] if you want a list or iterate over it if you don't need to store the list.
Assuming I have the following array in a JSON file:
[
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
]
How to get the object with more properties? In this example I should get the third item: { id: 3, name: 'foo', nick: 'bar' }
If there is another object with 3 properties, I can get two results or the last found, it doesn't matter, my purpose is to know all properties an object can have.
To cope with multiple results, you could use filter.
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' },
{ id: 6, name: 'another 3', nick: '3'}
]
const mx = Math.max(...data.map(m => Object.keys(m).length));
const res = data.filter(f => Object.keys(f).length === mx)
console.log(res);
You can create an array and put values based on key length.
Since you want objects with most keys, you can get the last item.
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
];
var res = data.reduce((a, c) => {
const len = Object.keys(c).length;
a[len] = a[len] || [];
a[len].push(c);
return a;
}, []).pop();
console.log(res);
You can use reduce and Object.keys() to return the object which has more length.
Try the following way:
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
]
var res = data.reduce((a, c) => {
return Object.keys(a).length > Object.keys(c).length ? a : c;
})
console.log(res);
let biggestObj = {};
for(let el of array){
if(Object.keys(el).length > Object.keys(biggestObj).length){
biggestObj = el;
}
}
This should do the job!
Let's say we have an array that looks like this:
[
{
id: 0,
name: 'A'
},
{
id: 1,
name:'A'
},
{
id: 2,
name: 'C'
},
{
id: 3,
name: 'B'
},
{
id: 4,
name: 'B'
}
]
I want to keep only this objects that have the same value at 'name' key. So the output looks like this:
[
{
id: 0,
name: 'A'
},
{
id: 1,
name:'A'
},
{
id: 3,
name: 'B'
},
{
id: 4,
name: 'B'
}
]
I wanted to use lodash but I don't see any method for this case.
You can try something like this:
Idea:
Loop over the data and create a list of names with their count.
Loop over data again and filter out any object that has count < 2
var data = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }];
var countList = data.reduce(function(p, c){
p[c.name] = (p[c.name] || 0) + 1;
return p;
}, {});
var result = data.filter(function(obj){
return countList[obj.name] > 1;
});
console.log(result)
A lodash approach that may (or may not) be easier to follow the steps of:
const originalArray = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }];
const newArray =
_(originalArray)
.groupBy('name') // when names are the same => same group. this gets us an array of groups (arrays)
.filter(group => group.length == 2) // keep only the groups with two items in them
.flatten() // flatten array of arrays down to just one array
.value();
console.log(newArray)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
A shorter solution with array.filter and array.some:
var data = [ { ... }, ... ]; // Your array
var newData = data.filter((elt, eltIndex) => data.some((sameNameElt, sameNameEltIndex) => sameNameElt.name === elt.name && sameNameEltIndex !== eltIndex));
console.log("new table: ", newTable);
You could use a hash table and a single loop for mapping the objects or just an empty array, then concat the result with an empty array.
var data = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }],
hash = Object.create(null),
result = Array.prototype.concat.apply([], data.map(function (o, i) {
if (hash[o.name]) {
hash[o.name].update && hash[o.name].temp.push(hash[o.name].object);
hash[o.name].update = false;
return o;
}
hash[o.name] = { object: o, temp: [], update: true };
return hash[o.name].temp;
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }