Lodash. How to groupBy except undefined - javascript

Trying to group up array of object to get unique properties with Lodash
But it takes undefined like unique property if property does not exists
Is there way how to avoid it? and left only existing properties ?
So i'm going to achieve only My Office 1 but with my solution getting
undefined and My Office 1
Example array
[
{office: null},
{office:
{
name: 'My Office 1'
}
}
]
code
Object.keys(_.groupBy(arr, 'office.name')).map((office, index) { ... }

You can just filter out the objects, which do not have the path.
let objects = [
{office: null},
{office: {name: 'My Office 1'}},
{office: {name: 'My Office 2'}},
{office: {name: 'My Office 1'}},
];
let path = 'office.name';
let grouped = _(objects)
.filter(object => _.has(object, path))
.groupBy(path)
.value();
console.log(grouped);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Related

'Convert' a many object in single array to have 'One Array One Object'

How to create a 'One Array One Object' if i have an array variable like this
const honey = [
{
name: "Caroline",
location: "Colorado",
},
{
name: "Sarashina",
location: "Tokyo",
},
];
to like this?
[{
name : 'Caroline',
location : 'Colorado',
}],
[{
name : 'Sarashina',
location : 'Tokyo',
}];
You could use Array.map function to achieve this functionality.
const honey = [{
name : 'Caroline',
location : 'Colorado',
},
{
name : 'Sarashina',
location : 'Tokyo',
}];
// Here item is each element object of the above array.
// By doing item => [item], we are taking the item
// and return an array of length 1 containing that object
const newarray = honey.map(item => [item]);
console.log(newarray);
Your desired output is not a valid structure - I imagine you either want an array that contains other arrays (each with 1 element), or you want to break out each item in the original array into it's own single-element array variable.
For the latter, you could use Array.prototype.map and then destructure each into its own variable, for example:
const honey = [{
name: 'Caroline',
location: 'Colorado',
},
{
name: 'Sarashina',
location: 'Tokyo',
}
];
// Create a new array of arrays then destructure each sub-array into its own variable
const [caroline, sarashina] = honey.map(obj => [obj]);
console.log(caroline);
console.log(sarashina);

How to get nested array item where object property id is "random" and change the name property

I have a nested array of objects. How to get nested array item where object property id is "random" and set name to "you won it"
The structure looks like this:
[
{
itemsBundle [
{
id: 'selection'
name: 'failed'
}
{
id: 'random'
name:'win'
}
]
basketId: 'item'
basketName
tags[{}{}]
}
{}
{}
]
I need somehow get the object inside the main array where nested itemsBundle array of objects containes object with id 'random' and then for that itemBundle's single object where id is 'random' set name from win to you won it. I thought about using nested map() with filter() or nested loops but not sure which option will be the best and how can this results be achieved with less complicated way. The only 3rd party library that I am using is lodash.
Working example with flatMap and find. BUT it is mutating the main content, you have to clone deep you list first.
Improve your question next time :)
const aa = [
{
itemsBundle: [
{
id: "selection",
name: "failed"
},
{
id: "random",
name: "win"
}
],
basketId: "item",
basketName: "",
tags: [{}, {}]
},
{},
{}
];
const items = aa.flatMap(a => a.itemsBundle || [])
const itemFound = items.find(item => item.id === 'random')
itemFound.name = 'you win'
console.log(aa[0].itemsBundle[1])

How to, with a one-lined solution, can I reduce an object containing arrays of arrays of objects to the inner objects? [duplicate]

This question already has answers here:
Why no Array.prototype.flatMap in javascript?
(8 answers)
Closed 3 years ago.
I have the following object structure:
regions: [
{
name: reg1,
subregions: [
{name: subreg11},
{name: subreg12}
]
},
{
name: reg2,
subregions: [
{name: subreg21},
{name: subreg22}
]
}
]
What I'd like, is to extract an array of the subregions, like so:
[{name: subreg11}, {name: subreg12}, {name: subreg21}, {name: subreg22}]
...and I'd like to do that with a one-line array.prototype-function, like .filter, .map, .forEach etc.
I've managed to do it in three lines like this (including the return-statement, which I'd like to have in the one-liner):
let subregions = []
regions.forEach(reg => reg.subregions.forEach(subregion => subregions.push(subregion)))
return locs
Does anyone know how I could do this?
EDIT July 2020: I see the question has been marked as a duplicate by #nina-sholz, but I don't believe this is true. The linked question asks "Why no Array.prototype.flatMap in javascript?", while my question was a description of a way to solve a problem. I didn't request the flatMap function specifically, and marking this as a duplicate simply says "you should have intuitively known that Array.prototype.flatMap was the thing you were looking for".
flatMap and extract the subregions property:
const regions = [
{
name: 'reg1',
subregions: [
{name: 'subreg11'},
{name: 'subreg12'}
]
},
{
name: 'reg2',
subregions: [
{name: 'subreg21'},
{name: 'subreg22'}
]
}
];
const output = regions.flatMap(({ subregions }) => subregions);
console.log(output);
You can also use reduce:
const regions = [
{
name: 'reg1',
subregions: [
{name: 'subreg11'},
{name: 'subreg12'}
]
},
{
name: 'reg2',
subregions: [
{name: 'subreg21'},
{name: 'subreg22'}
]
}
];
const output = regions.reduce((a, { subregions }) => a.concat(subregions), []);
console.log(output);
Pretty much any Javascript code can be reduced into a single line, but it won't necessarily be readable.
You can use map and flat
const regions = [{name: 'reg1',subregions: [{name: 'subreg11'},{name: 'subreg12'}]},{name: 'reg2', subregions: [{name: 'subreg21'},{name: 'subreg22'}]}];
const output = regions.map(({ subregions }) => subregions).flat();
console.log(output);

Assert sorted array of objects [duplicate]

This question already has an answer here:
Map and Sort in one iteration in Javascript?
(1 answer)
Closed 4 years ago.
I'm doing unit testing using javascript testing framework (mocha, chai, etc). How can I assert my array of objects using its name?
I can successfully sort this using localeCompare but I'm not getting what I wanted on my test. It just returns 1 or -1.
Here's my a sample of what I want to sort.
var Stuffs = [
{ name: "PWE", address: "1234567890" },
{ name: "NSA", address: "1234567890" },
{ name: "AVE", address: "1234567890" },
{ name: "QRE", address: "1234567890" },
]
How can I assert this to ["AVE", "NSA", "PWE", "QRE"] ?
To get your desired Array, you can use:
Stuffs.map(({ name }) => name).sort();
You could also use reduce() method of array and then sort().
DEMO
var Stuffs = [{ name: "PWE", address: "1234567890" },
{ name: "NSA", address: "1234567890" },
{ name: "AVE", address: "1234567890" },
{ name: "QRE", address: "1234567890" }];
let sortedArr = Stuffs.reduce((r,{name})=>r.concat(name),[]).sort((a,b)=>a.localeCompare(b));
console.log(sortedArr);
Using chai
assert.deepInclude(Stuffs, {name: "AVE"});
assert.deepInclude(Stuffs, {name: "NSA"});
assert.deepInclude(Stuffs, {name: "QRE"});
assert.deepInclude(Stuffs, {name: "PWE"});
Edit: I may have misunderstood it to mean, how can assert that the array has those values.
First you would need a sorted array to compare it to. The original array needs to be cloned (shallow is fine for this) since sort will modify the array it is called on. Using from to generate the clone, we can then alphabetically sort the new array to get the desired order.
const sortedStuffs = Array.from(stuffs).sort(({name: a}, {name: b}) => a.localeCompare(b));
Finally using every, we can compare the names for each element to see if they match. As soon as one fails, the returned value will be false
stuffs.every(({name}, i) => name === sortedStuffs[i].name);
Full working example:
const stuffs = [
{name: "PWE", address: "1234567890"},
{name: "NSA", address: "1234567890"},
{name: "AVE", address: "1234567890"},
{name: "QRE", address: "1234567890"}
];
const sortedStuffs = Array.from(stuffs).sort(({name: a}, {name: b}) => a.localeCompare(b));
const isSorted = stuffs.every(({name}, i) => name === sortedStuffs[i].name);
console.log(isSorted);

Deep reduce (pluck maybe?) of array of objects

I have lodash to use for this (or underscore). I am trying to take an array of objects and turn them into a boiled down array of objects. Let me show you.
$scope.myObject = [{
name: 'Name1',
subs: [
{
name: 'Sub 1',
subs: [
{
name: 'Sub 1-1',
apps: [
{
name: 'App 1'
}
]
}
],
apps: [
{
name: 'App'
}
]
}
That's the original objects (one node of it at least). And My desired effect is to boil it down to an array of objects that are just the 'apps'. As you see here the apps can fall on any level - so it needs to be some kind of a deep search/reduce. These objects can potentially go 10 levels deep and have an app array on any level. So I'm trying to boil it down to a flat array of just apps, so for example this object would turn into:
[{'name' : 'App 1'},{'name' : 'App'}];
I'm still pretty new to this kind of object manipulation so I could use some guidance. Thanks!
function pluckRecursive(input, prop, collect) {
collect = collect || [];
if (_.isArray(input)) {
_.forEach(input, function (value, key) {
pluckRecursive(value, prop, collect);
})
} else if (_.isObject(input)) {
_.forEach(input, function (value, key) {
if (key === prop) {
collect.push(value);
} else {
pluckRecursive(value, prop, collect);
}
})
}
return collect;
};
when used like
pluckRecursive($scope.myObject, 'apps')
returns:
[[{"name":"App 1"}], [{"name":"App"}]]
Use _.flatten() on that to get rid of the nested arrays.
You may use deep-reduce like this:
const deepReduce = require('deep-reduce')
let apps = deepReduce($scope.myObject, (arr, value, path) => {
if (path.match(/subs\.\d+\.apps\.\d+$/) {
arr.push(value)
}
return arr
}, [], 'subs')
Explanation of the code:
(arr, value, path) => ... is the reducer function. It should always return the accumulated value, here the array of apps. The accumulated value is passed to the reducer as the first argument.
value, is any value in your object tree. It may be the subs array, objects inside the subs array, or leaf values like subs.0.name.
path is where the current value passed is found. For example subs.0.subs.0.apps. Keys of arrays are digits, here 0.
subs\.\d+\.apps\.\d+$ matches any path that ends in subs.digit.apps.digit. I've omitted ^ from the start, since some apps are nested in subs subs. You may try out the regexp here: https://regex101.com/r/n1RfVv/1
[] is the initial value of arr.
'subs' tell deepReduce to start at that path, meaning that name or any other property in the root object will not be traversed.
Speed
Depending on the size of your objects, this might be slow, as deep-reduce traverses the whole object tree. If possible, define the starting path to limit traversal, like 'subs' is given in example above.
If your data source is not changing much, you might want to scrape the data source, clean it up, and provide your own api.
Inner workings
If you are interested in how deep-reduce works, you can read the code here: https://github.com/arve0/deep-reduce/blob/master/index.ts
Here are two solution using object-scan. The first one searches for apps anywhere, the second one only in nested subs.
// const objectScan = require('object-scan');
const myObject = [{ name: 'Name1', subs: [{ name: 'Sub 1', subs: [{ name: 'Sub 1-1', apps: [{ name: 'App 1' }] }], apps: [{ name: 'App' }] }] }];
console.log(objectScan(['**.apps[*]'], { rtn: 'value' })(myObject));
// => [ { name: 'App' }, { name: 'App 1' } ]
console.log(objectScan(['**(^subs$).apps'], { rtn: 'value', useArraySelector: false })(myObject));
// => [ { name: 'App' }, { name: 'App 1' } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
You can do this natively using _.get
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
_.get(object, ['a', '0', 'b', 'c']);
// => 3
_.get(object, 'a.b.c', 'default');
// => 'default'

Categories

Resources