Ordering based on array of objects - javascript

I have to define a property sort of an array, using based an array of other objects that have 2 properties called source and target, where the source is the first element and target will be the right next.
My current array is filled in this way:
[{"id":25075,"sort":1},{"id":25076,"sort":2},{"id":25077,"sort":null}]
But based on the source target that I have it should be like this
[{"id":25075,"sort":1},{"id":25076,"sort":3},{"id":25077,"sort":2}]
For a better understanding the source target I have is it:
[{"source":25075,"target":25077},{"source":25077,"target":25076}]
Does somebody know what would be the best way to handle it?

That's is what you are looking for ?
const array = [
{ source: 25075, target: 25077 },
{ source: 25077, target: 25076 },
];
const result = array.reduce((acc, { source, target }, index) => {
if (array.length && array.length > index + 1) {
return [...acc, { id: source, sort: index + 1 }];
} else if (array.length) {
return [
...acc,
{ id: source, sort: index + 1 },
{ id: target, sort: index + 2 },
];
}
return acc;
}, []);
console.log("result", result);
At the end we will have the { id: value, sort: position } array you are looking for ?
This code doesn't handle all the cases (with duplicate or other stuff ;))

I don't really understand your problem but is that solving the issue ?
const array = [
{ id: 25075, sort: 1 },
{ id: 25076, sort: 3 },
{ id: 25077, sort: 2 },
];
const result = [];
array.sort((a, b) => (a.sort < b.sort ? -1 : 1));
array.map((v, index) => {
if (array.length > index + 1) {
result.push({ source: v.id, target: array[index + 1].id });
}
});
console.log("result", result);
Instead of the map you can also use reduce and fill an accumulator.

Related

Javascript doesn't sort list items properly

I have - array of objects - list items, I sort these items by fieldName. Normally it seems it works fine, but on some items it behaves strange and doesn't sort items properly.
Here is the code that I am making sorting:
elements.slice(0).sort((a, b) => {
if (a[fieldName] === '' || a[fieldName] == null) return 1;
if (b[fieldName] === '' || b[fieldName] == null) return -1;
return (
itemSort
? a[fieldName]?.toLowerCase() < b[fieldName]?.toLowerCase()
: a[fieldName]?.toLowerCase() > b[fieldName]?.toLowerCase()
)
? 1
: -1;
})
itemSort is a boolean and I decide to make A-Z or Z-A sorting.
Here is a picture from strange behaviour, I only see the wrong sorting on these items.
Here is an example of elements
[
{
icon: "IssueTracking"
id: "62a0868c2b2b180061ab05d8"
name: "[DEMO ASLC] All Issues"
type: "sheet"
updatedAt: "2022-12-05T15:17:23.072Z"
url: "/admin/documents/edit/62a0868c2b2b180061ab05d8"
},
{
icon: "..."
id: "..."
name: "..."
type: "..."
updatedAt: "..."
url: "..."
},
...
]
.sort() method modifies array itself, so you need to copy the array into a new array if you would like to keep your original array order in place.
const elementArray = [
{ name: "abc" },
{ name: "abb" },
{ name: "cc" },
{ name: "1bb" },
{ name: "4bc" },
{ name: "abb4" },
{ name: "" },
];
const sortItems = (elements, asc = true) => {
const sortedArray = [...elements];
sortedArray.sort((a, b) => {
let sortResult = a.name?.toLowerCase() > b.name?.toLowerCase() ? 1 : -1;
return asc ? sortResult : sortResult * -1
});
return sortedArray;
};
console.log(`descending: ${JSON.stringify(sortItems(elementArray, false))}`);
console.log(`ascending: ${JSON.stringify(sortItems(elementArray))}`);
One of the best way to do this is to use the Lodash sortBy method.
You can install this library with npm or yarn and simply do _.sortBy(elements, 'fieldName')

How to add a value to an array in a specific location with JS

I have an array of objects:
const array = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 }
];
and I need to add another entry to it, but it needs to be placeable within any location in the array. So for example:
array.push({ id: 5, after_id: 2 }); and this should place the new entry between ids 2 and 3. Is there some standard way of doing this?
#p.s.w.g Has posted what is probably the best solution in a comment already, but I thought I'd post my original solution here as an answer now this is reopened.
You can use some to iterate through the array until the correct index is found, then you can slice the array and insert the item at the relevant index:
const arrayTest = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
const insertAfterId = (array, item, idAfter) => {
let index = 0;
array.some((item, i) => {
index = i + 1;
return item.id === idAfter
})
return [
...array.slice(0, index),
item,
...array.slice(index, array.length),
];
};
const result = insertAfterId(arrayTest, {
id: 6
}, 2)
console.dir(result)

How to perform cummulative sum operation on inner object property value using javascript/typescript/angular5

I need to get sum of all successCount from the below array object.
var successCount;//I want count of all successCount attributes from the below object
var accordianData = [
{
name: "Start of Day",
subItemsData: [
{
title: "Position",
items: [
{ title: "XYZ",successCount:3,warningCount:1,status:"success"},
{ title: "ABC",successCount:3,warningCount:1,status:"success"},
{ title: "DDD",successCount:3,warningCount:1,status:"success"},
{ title: "ABCD",successCount:10,warningCount:1,status:"success"}
]
},
{
title: "Trades",
items: [
{ title: "DDD",successCount:3,warningCount:0,status:"success"},
{ title: "DDD",successCount:30,warningCount:0,status:"success"}
]
}
]
}];
Here is a general solution that will traverse your data (even of unknown structure) and sum up successCounts wherever they are.
const dfs = (sum: number, node: any): number =>
typeof node === 'object'
? Object.keys(node).map(key => node[key]).reduce(dfs, sum + (node['successCount'] || 0))
: Array.isArray(node) ? node.reduce(dfs, sum) : sum
alert(dfs(0, accordianData));
Use Array.reduce
var accordianData = [{name:"StartofDay",subItemsData:[{title:"Position",items:[{title:"XYZ",successCount:3,warningCount:1,status:"success"},{title:"ABC",successCount:3,warningCount:1,status:"success"},{title:"DDD",successCount:3,warningCount:1,status:"success"},{title:"ABCD",successCount:10,warningCount:1,status:"success"}]},{title:"Trades",items:[{title:"DDD",successCount:3,warningCount:0,status:"success"},{title:"DDD",successCount:30,warningCount:0,status:"success"}]}]},{name:"IntraofDay",subItemsData:[{title:"Position",items:[]},{title:"MarketData",items:[]},{title:"Trades",items:[]}]}];
let successCount = accordianData.reduce((a, {subItemsData}) =>
subItemsData.reduce((ac, {items}) =>
items.reduce((acc, v) =>
v.status === "success" ? acc + v.successCount : acc
, ac)
,a)
, 0);
console.log(successCount);

Count elements that have the same value for a specific property and put the result in an array of objects

Using Array.reduce, I am trying to count the elements that have the same value for a specific property. I want to put the result in an array of objects containing a property for the value of the grouped by property and another one for the count. How can I do this easily in javascript ?
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [
{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}
]
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
console.log(categoryLessonCount[CATEGORY.STRATEGY])
console.log(categoryLessonCount[CATEGORY.CONTENT])
Actual categoryLessonCount value :
Object
{
STRATEGY: 1,
CONTENT: 2
}
Wanted categoryLessonCount value :
Array
[
{
title: 'STRATEGY',
count: 1
}, {
title: 'CONTENT',
count: 2
}
]
You already got the what you want just transform it into an array
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}]
let count = lessons.reduce(function(acc, lesson) {
acc[lesson.category] ? acc[lesson.category] ++ : acc[lesson.category] = 1
return acc
}, {})
// transform count into what you want
let categoryLessonCount = [];
for (let cat in count) {
categoryLessonCount.push({
'title': cat,
'count': count[cat]
});
}
console.log(categoryLessonCount)
Something like this should work:
let categoryLessonCount = lessons.reduce(function(acc, lesson) {
let found = false
for (const item of acc) {
if (item.title === lesson.category) {
item.count++
found = true
}
}
if (!found) {
acc.push({
title: lesson.category,
count: 1
})
}
return acc
}, [])
Your main issue is that your accumulating an object but expecting an array (note the final argument to reduce).
Short solution using Object.keys and Array.prototype.map functions:
...
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
let counts = Object.keys(categoryLessonCount).map(
(k) => ({title: k, count: categoryLessonCount[k]})
)
console.log(counts);

How to combine _.map and _.filter in a more efficient way?

I am using Lodash in my Angular project and I was wondering if there is a better way to write the following code:
$scope.new_arr = _.map(arr1, function(item){
return _.assign(item, {new_id: _.find(arr2, {id: item.id})});
});
$scope.new_arr = _.filter($scope.new_arr, function (item) {
return item.new_id !== undefined;
});
I am trying to combine values from one array to same objects in other array, and I want to ignore the objects that not appear in both arrays (it is something like join or left outer join in the sql language).
Here is a fiddle with an example of this code: Click me!
i think is better to use chaining
$scope.new_arr = _.chain(arr1)
.map(function(item) {
return _.merge(
{}, // to avoid mutations
item,
{new_id: _.find(arr2, {id: item.id})}
);
})
.filter('new_id')
.value();
https://jsfiddle.net/3xjdqsjs/6/
try this:
$scope.getItemById = (array, id) => {
return array.find(item => item.id == id);
};
$scope.mergeArrays = () => {
let items_with_ids = arr1.filter(item => !_.isNil($scope.getItemById(arr2,item.id)));
return items_with_ids.map(item => _.assign(item, {new_id: $scope.getItemById(arr2,item.id)}));
};
The answers provided here are all runtime of O(n^2), because they first run an outer loop on the first array, with an inner loop on the second array. You can instead run this in O(n). First, create a hashmap of all the ids in arr2 in a single loop; this will allow us an order 1 lookup. In the second loop on arr1, check this hashmap to determine if those items exist with O(n). Total Complexity is n + n = 2n, which is just O(n).
// provision some test arrays
var arr1 = [
{
id: 2
},
{
id: 4
},
{
id: 6
}
]
var arr2 = [
{
id: 3
},
{
id: 4
},
{
id: 5
},
{
id: 6
}
]
// First, we create a map of the ids of arr2 with the items. Complexity: O(n)
var mapIdsToArr2Items = _.reduce(arr2, function(accumulator, item) {
accumulator[item.id] = item;
return accumulator;
}, {});
// Next, we use reduce (instead of a _.map followed by a _.filter for slightly more performance.
// This is because with reduce, we loop once, whereas with map and filter,
// we loop twice). Complexity: O(n)
var combinedArr = _.reduce(arr1, function(accumulator, item) {
// Complexity: O(1)
if (mapIdsToArr2Items[item.id]) {
// There's a match/intersection! Arr1's item matches an item in arr 2. Include it
accumulator.push(item);
}
return accumulator;
}, []);
console.log(combinedArr)
You could first make a Map with arr1 and then map the items of arr2 with the properties of arr1.
var arr1 = [{ id: 1, title: 'z' }, { id: 2, title: 'y' }, { id: 3, title: 'x' }, { id: 4, title: 'w' }, { id: 5, title: 'v' }],
arr2 = [{ id: 2, name: 'b' }, { id: 3, name: 'c' }, { id: 4, name: 'd' }, { id: 5, name: 'e' }],
map = new Map(arr1.map(a => [a.id, a])),
result = arr2.map(a => Object.assign({}, a, map.get(a.id)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources