Related
I have a list look like:
const initArray = [
{
id: 0,
},
{
id: 1,
},
{
id: 2,
},
{
id: 3,
},
];
A selected list look like:
const selectedList = [
{
id: 2,
},
];
And the desired data has been sorted:
const outPut= [
{
id: 2,
},
{
id: 0,
},
{
id: 1,
},
{
id: 3,
},
];
I'm in trouble right now, so I can't figure it out yet.
Can you share some solutions?
You could take an object which keeps the order of the first objects and sort the rest after.
const
data = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }],
selectedList = [{ id: 2 }],
order = Object.fromEntries(selectedList.map(({ id }, i) => [id, i + 1]));
data.sort((a, b) => (order[a.id] || Number.MAX_VALUE) - (order[b.id] || Number.MAX_VALUE));
console.log(data);
Using Set and Array#map, get set of ids to prioritize
Using Array#sort, sort the items using the above set
const _sort = (arr = [], selected = []) => {
const priority = new Set( selected.map(({ id }) => id) );
return [...arr].sort(({ id: a }, { id: b }) => priority.has(b) - priority.has(a));
}
const
initArray = [ { id: 0 }, { id: 1 }, { id: 2 }, { id: 3 } ],
selectedList = [ { id: 2 } ];
console.log( _sort(initArray, selectedList) );
I have an array and want to get just object { id: 4, name: 'name4' },
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
},
I try in this way.
const name = 'name4';
example.forEach((item) => item.items.find((i) => i.name === name));
But get undefined.
You can using flatMap() to do it
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
}]
const name = 'name4';
let result = example.flatMap(e => e.items).filter(d => d.name == name)
console.log(result)
this way...?
const example =
[ { id : '1234'
, desc : 'sample1'
, items:
[ { id: 1, name: 'name1' }
, { id: 2, name: 'testItem2' }
] }
, { id : '3456'
, desc : 'sample2'
, items:
[ { id: 4, name: 'name4' }
, { id: 5, name: 'testItem5' }
] } ];
const rechName = (s,arr) =>
arr.find( x => // find the first parent object
x.items.some( y => // containing the search
y.name === s )
)?.items // if one
.find( z => z.name === s ); // find it in!
console.log( rechName('name4', example) ) // -> { id: 4, name: 'name4' }
console.log( rechName('abc', example) ) // -> undefined
forEach doesn't do what you think it does. From the docs:
The forEach() method executes a provided function once for each array element.
...
Return value
undefined.
So if you want to use forEach you need to save the value:
const example =
[ { id : '1234'
, desc : 'sample1'
, items:
[ { id: 1, name: 'name1' }
, { id: 2, name: 'testItem2' }
] }
, { id : '3456'
, desc : 'sample2'
, items:
[ { id: 4, name: 'name4' }
, { id: 5, name: 'testItem5' }
] } ]
const results = []; // Store matches here
const name = 'name4';
example.forEach((item) => {
const res = item.items.find((i) => i.name === name);
if (res !== undefined) {
results.push(res);
}
});
console.log(results);
IMHO I would suggest a more functional approach using flatMap and filter instead of forEach.
Lastly, note that in my above snippet, I'm storing the results in an array as it's not entirely clear to me that you won't have multiple matches per your example. But if you're sure that you will only ever have one result then a simple for loop works better, especially if you have a large array of items:
let result = null;
for (let i = 0; i < example.length; i++) {
const res = example[i].items.find((j) => j.name === name);
if (res !== undefined) {
result = res;
break; // No need to iterate further
}
}
console.log(result);
You could use a recursive search function. Here's a detailed example:
// Applies the function recursively from the top of the data tree ("depth-first")
const
data = getData(),
id = 4,
result = findById(data, id);
console.log(result ?? `No item with id ${id} found`);
// Defines the function
function findById(haystack, needleId){
let needle = null; // Defaults to null if no match at or below this level
// Iterates through all the items at this level
for(const item of haystack){
if(item.id == needleId){
// Qapla': Quits early, passes honorable item up to previous level
needle = item;
return needle;
}
else {
// Checks children, grandchildren, etc, before continuing iteration
const nextHaystack = item.items;
if(nextHaystack?.length){
needle = findById(nextHaystack, needleId); // Recursive call
}
}
// Done searching children, continues to next iteration at this level
}
// Done searching this level, returns result up to previous level
return needle;
}
// Gets the initial data
function getData(){
return [
{
id: '1234',
desc: 'sample1',
items: [ { id: 1, name: 'name1' }, { id: 2, name: 'testItem2' } ]
},
{
id: '3456',
desc: 'sample2',
items: [ { id: 4, name: 'name4' }, { id: 5, name: 'testItem5' } ]
}
];
}
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
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.
I have a specific case and I don't even know if it is possible to achieve.
Given the input array.
var originalArr = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[{ ID: 1, name: 'Lettuce' }, { ID: 2, name: 'Brocoli' }]
];
I would like to iterate over the inner arrays and pick the ID's from objects then create new one in place of array. So my output should look something like this.
var output = [
{
'1': {
name: 'Lettuce',
ID: 1
},
'2': {
name: 'Brocoli',
ID: 2
}
},
{
'3': {
name: 'Beef',
ID: 3
},
'4': {
name: 'Macaroni',
ID: 4
},
'5': {
name: 'Sauce#1'
}
}
];
It is easy to iterate over the inner arrays with map but how can I create new Object in place of the array and have its key value dynamically pulled up ? And is it even possible given my input to produce the desired output.
Use map and reduce
originalArr.map( s => //iterate outer array
s.reduce( (acc, c) => ( //iterate inner array using reduce
acc[c.ID] = c, acc //assign the id as key to accumulator and return the accumulator
) , {}) //initialize accumulator to {}
)
Demo
var originalArr = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[{ ID: 1, name: 'Lettuce' }, { ID: 2, name: 'Brocoli' }]
];
var output = originalArr.map( s => s.reduce( (acc, c) => ( acc[c.ID] = c, acc ) , {}) );
console.log(output);
You can achieve using recursion with pure javascript
var originalArr = [
[{
ID: 3,
name: 'Beef'
}, {
ID: 4,
name: 'Macaroni'
}, {
ID: 5,
name: 'Sauce#1'
}],
[{
ID: 1,
name: 'Lettuce'
}, {
ID: 2,
name: 'Brocoli'
}]
]
function bindInObject(object, array) {
for (var i = 0; i < array.length; i++) {
var it = array[i];
if (it instanceof Array) {
bindInObject(object, it);
} else {
var id = it.ID;
object[id] = it;
}
}
}
var output = {};
bindInObject(output, originalArr);
console.log(output)
const original_array = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[
{ ID: 1, name: 'Lettuce' },
{ ID: 2, name: 'Brocoli' }
]
]
let new_array = []
for (let i=0; i < original_array.length; i++) {
if (original_array[i + 1]) new_array =
new_array.concat(original_array[i].concat(original_array[i+1]))
}
let output = []
for (let i=0; i<new_array.length; i++) {
output.push({[new_array[i].ID]: new_array[i]})
}